21

Let's say I have a text file like:

john
george
james
stewert

with every name on a separate line.

I want to read the lines of this text file and create a text file for each name, like: john.txt, george.txt etc.

How can I do this in Bash?

  • 2
    Do you need the extension? There is no reason for text files to have a txt extension in Linux. – terdon Nov 22 '15 at 19:04
18

#1 Using Bash + touch:

while read line; do touch "$line.txt"; done <in
  • while read line; [...]; done <in: This runs read until read itself returns 1, which happens when when the end of the file is reached; the input for read is read from a file named in in the current working directory instead of from the terminal due to the <in redirection;
  • touch "$line.txt": This runs touch on the expanded value of $line.txt, which is the content of line followed by .txt; touch will create the file if not existing and update its access time if existing;

#2 Using xargs + touch:

xargs -a in -I name touch name.txt
  • -a in: makes xargs read its input from a file named in in the current working directory;
  • -I name: makes xargs replace every occurence of name with the current line of input in the following command;
  • touch name: Runs touch on the replaced value of name; it will create the file if not existing and update its access time if existing;
% ls
in
% cat in
john
george
james
stewert
% while read line; do touch "$line.txt"; done <in
% ls
george.txt  in  james.txt  john.txt  stewert.txt
% rm *.txt
% xargs -a in -I name touch name.txt
% ls
george.txt  in  james.txt  john.txt  stewert.txt
  • how will read line actually read the line? @kos – kashish Nov 22 '15 at 14:52
  • @kashish <in after done makes read in the while loop condition read and store one line from in in line at each iteration; this way, touch "$line.txt" inside the loop will be expanded to the read line with .txt at the end. – kos Nov 22 '15 at 14:57
  • will while read line; do touch "$line.txt"; done <in alone do the job? Where is the source file? – kashish Nov 22 '15 at 15:05
  • @kashish Sure, they're alternative methods: you're meant to use either one or the other. – kos Nov 22 '15 at 15:07
  • 1
    @kashish that's how read works. See help read. – terdon Nov 22 '15 at 17:07
18

In this particular case, where you only have a single word per line, you can also do:

xargs touch < file

Note that this will break if your file names can contain spaces. For such cases, use this instead:

xargs -I {} touch {} < file

Just for fun, here are a couple of other approaches (both of which can handle arbitrary file names, including lines with spaces):

  • Perl

    perl -ne '`touch "$_"`' file
    
  • Awk

    awk '{printf "" > $0}' file 
    

Note that on Linux and similar systems, the extension is optional for the vast majority of files. There is no reason to add a .txt extension to a text file. You are free to do so but it makes no difference at all. So, if you want the extension anyway, use one of:

xargs -I {} touch {}.txt < file
perl -ne '`touch "$_.txt"`' file
awk '{printf "" > $0".txt"}' file 
7

AWK is also appropriate for this task:

testerdir:$ awk '{system("touch "$0)}' filelist

testerdir:$ ls
filelist  george  james  john  stewert

testerdir:$ awk '{system("touch "$0".txt")}' filelist                          

testerdir:$ ls
filelist  george.txt  james.txt  john.txt  stewert.txt
george    james       john       stewert

Another way, tee. Notice that this approach will break if a line contains more than one string in the filelist.

testerdir:$ echo "" | tee $(cat filelist)


testerdir:$ ls
filelist  george  james  john  stewert

Alternatively, </dev/null tee $(cat filelist) can be done as well, if you want to avoid piping

cp /dev/null approach (As I demonstrate this does work with filenames that contain spaces):

testerdir:$ cat filelist | xargs -I {}  cp /dev/null  "{}"                     

testerdir:$ ls
filelist  FILE WITH SPACES  george  james  john  stewert

testerdir:$ ls FILE\ WITH\ SPACES                                              
FILE WITH SPACES
  • 1
    @kos if each line in file contains exactly one string per line, then it creates a file per line. It's effectivelly the same as echo "" | tee file1 file2 file2 . But it does break if filenames contain spaces. – Sergiy Kolodyazhnyy Nov 22 '15 at 17:38
7

Let's say I have a text file …

Let's say, I have an answer ;)

awk '{system("touch \""$0".txt\"")}' file

Waterproof also with spaces and with a suffix =)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.