Batch create files with name and content based on input file
Batch create files with name and content based on input file
I am a mac OS user trying to batch create a bunch of files. I have a text file with column of several hundred terms/subjects, eg:
hydrogen
oxygen
nitrogen
carbon
etcetera
I want to programmatically fill a directory with text files generated from this subject list. For example, "hydrogen.txt" and "oxygen.txt" and so on, with each file created by iterating through the lines of my list_of_names.txt file. Some lines are one word, but other lines are two or three words (eg: "carbon monoxide"). This I have figured out how to do:
awk 'NF>0' list_of_names.txt | while read line; do touch "$line.txt"; done
Additionally I need to create two lines of content within each of these files, and the content is both static and dynamic...
# filename
#elements/filename
...where in the example above the pound sign ("#") and "elements/" would be the same in all of the files created, but "filename" would be variable (eg: "hydrogen" for "hydrogen.txt" and "oxygen" for "oxygen.txt" etc). One further wrinkle is that if any spaces appear at all on the second line of content, there needs to be a trailing pound sign. For example:
# filename
#elements/carbon monoxide#
...although this last part is not a dealbreaker and I can use grep to modify list_of_names.txt such that phrases like "carbon monoxide" become "carbon_monoxide" and just deal with the repercussions of this later. (But if it is easy to preserve the spaces, I would prefer that.)
After a couple hours of searching and attempts to use sed, awk, and so on I am stuck at a directory full of files with the correct filename.txt format, but I can't get further that this. Mostly I think my efforts are failing because the solutions I can find for doing something like this are using commands I am not familiar with and they are structured for GNU and don't execute correctly in Terminal on Mac OS.
I am amenable to processing this in multiple steps (ie make all of the files.txt first, then run a second step to populate the content of the files), or as a single command that makes the files and all of their content simultaneously ('simultaneously' from a human timescale).
My horrible pseudocode (IN CAPS) for how this would look as 2 steps:
awk 'NF>0' list_of_names.txt | while read line; do touch "$line.txt"; done
awk 'NF>0' list_of_names.txt | while read line; OPEN "$line.txt" AND PRINT "# $linen#elements/$line"; IF $line CONTAINS CHARACTER " " PRINT "#"; done
2 Answers
2
You could use a simple Bash loop and create the files in one shot:
#!/bin/bash
while read -r name; do # loop through input file content
[[ $name ]] || continue # skip empty lines
output=("# $name") # initialize the array with first element
trailing=
[[ $name = *" "* ]] && trailing="#" # name has spaces in it
output+=("#elements/$name$trailing") # name doesn't have a space
printf '%sn' "$output[@]" > "$name.txt" # write array content to the output file
done < list_of_names.txt
Doing it in awk:
awk '
NF
trailing = (/ / ? "#" : "")
out=$0".txt"
printf("# %sn#elements/%s%sn", $0, $0, trailing) > out
close(out)
' list_of_names.txt
/^[[:space:]]*$/ next { do stuff
NF { do stuff
@EdMorton: thanks for your comments. Updated the answer.
– codeforester
Sep 4 at 17:39
Doing the whole job in awk will yield better performance than in bash, which isn't really suited to processing text like this.
It seems to me that this should cover the requirements you've specified:
awk '
out=$0 ".txt"
printf "# %sn#elements/%s%sn", $0, $0, (/ / ? "#" : "") >> out
close(out)
' list_of_subjects.txt
Though you could shrink it to a one-liner:
awk 'printf "# %sn# elements/%s%sn",$0,$0,(/ /?"#":"")>($0".txt");close($0".txt")' list_of_subjects.txt
Great. You may want to skip blank / empty lines.
– codeforester
Sep 4 at 3:54
Since the OP is not using GNU awk - that will fail with a "too many open files" error once you hit about 20 unique input lines (output files). You need to close() the output files as they change and then use
>> instead of > if they can occur again later.– Ed Morton
Sep 4 at 5:49
>>
>
@EdMorton, right you are, thanks for the reminder. Fixed.
– ghoti
Sep 4 at 12:29
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Rather than
/^[[:space:]]*$/ next { do stuffyou could just write it briefer asNF { do stuff.– Ed Morton
Sep 4 at 5:51