Batch create files with name and content based on input file

Viewed 460

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 "# ${line}\n#elements/${line}"; IF ${line} CONTAINS CHARACTER " " PRINT "#"; done
2 Answers

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 '%s\n' "${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("# %s\n#elements/%s%s\n", $0, $0, trailing) > out
          close(out)
       }
    ' list_of_names.txt

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 "# %s\n#elements/%s%s\n", $0, $0, (/ / ? "#" : "") >> out
    close(out)
  }
' list_of_subjects.txt

Though you could shrink it to a one-liner:

awk '{printf "# %s\n# elements/%s%s\n",$0,$0,(/ /?"#":"")>($0".txt");close($0".txt")}' list_of_subjects.txt
Related