3

I have ten files: firstFile.txt,...,tenthFile.txt

On the last line of the file I would like to append the name of the file.

i.e. for the first one the last line would be firstFile

I would like to perform this action in one line of code.

I'm pretty new so not sure what to try, my guess was something like echo * >> *.txt but wasn't surprised that didn't work

1
  • echo * >> *.txt - bash doesn't implicitly loop over multiple things for you. One >> redirect in one command will only ever open a single file for appending. And the echo command will only run once, with all those args. Mar 15, 2022 at 3:25

2 Answers 2

14

One can achieve this by, for example, using a for loop, which has the syntax:

for var in values; do things; done

This code puts the first of the list of values into var and performs things on it, then puts the next value into var and so on.

We often use this to do the same thing to a bunch of files, as you want to do here.

You can use some suitable expression to get Bash to create a list of your files (the list of values, as I've called it above). Here I suggest you use *File.txt. This would be expanded by the shell to a list of all the files in the current directory that end with File.txt. So the first bit of your code could be

for f in *File.txt; do

Instead of f you can use some other name for your variable, such as file. I suggest using a letter or word in lowercase (there are other variables already defined in the shell with uppercase words, and we don't want to over-ride them). When we call our variable in Bash, we precede its name with $. So you would refer to your variable with $f or $file, and the shell will expand this to the current value of that variable. To prevent further expansions on the result, we almost always use double quotes, so instead of writing $f, I would write "$f" when I call my variable.

To send some text into a file you can use simple tools like printf or echo with redirection. You have shown that you know how to use redirection to append content to a file. This: stuff > file replaces the contents of file with stuff and stuff >> file appends stuff to file.

We should be really careful with redirection as we can easily overwrite our important files - you won't even get a warning about it. So before you run such a powerful command, you might want to make a backup of your files in another directory, and/or do a dry run where you just check what files are going to be operated on, like this:

for f in *File.txt; do echo "$f"; done

The above simply returns the list of files that will be acted on.

So let's go back to how you are going to add text to the file. Since we already have the name of the file in our variable, we can use that to generate the text.

Here is how your code could look with echo:

for f in *File.txt; do echo "$f" >> "$f"; done

For firstFile.txt, that would run echo firstFile.txt >> firstFile.txt, and so on for all the other files matched by *File.txt.

But you mentioned that your files end with .txt and you don't want that to be in the appended text.

To avoid that you can perform some string manipulation

for f in *File.txt; do echo "${f%.*}" >> "$f"; done

This ${f%.*} trims characters from the last . character onwards from the string in $f, in this case the .txt.

You could also use printf. It's worth learning as it's more flexible than echo and, so I'm told, can be more reliable.

for f in *File.txt; do printf '%s\n' "${f%.*}" >> "$f"; done

'%s\n' is for formatting - %s means the text is a string (not a number, for example) and \n appends a newline after it (echo automatically appends a newline).

6
  • Thank you for this, it is extremely helpful to learn! I may have been implementing it wrong as I get a file called '*File' with every line being *File . do I need to change it to something? And I am using a test dir to do this thanks :)
    – kedoink
    Mar 14, 2022 at 11:20
  • @kedoink sorry - I edited my answer - I had not got your filenames right - what you describe happens because *File does not match anything. Change it to *File.txt or whatever matches your files (the files do need to be in the current directory of course) I also added how to avoid having .txt in the appended text.
    – Zanna
    Mar 14, 2022 at 11:22
  • 3
    @Zanna that might be a good opportunity to mention the bash shell's nullglob option. Great answer btw. Mar 14, 2022 at 11:40
  • Thank you very much @Zanna that is super helpful, you are brilliant!! Do you know any good resources to help me continue learning?
    – kedoink
    Mar 14, 2022 at 11:46
  • @kedoink thank you and you're welcome! You can try linuxclass.heinz.cmu.edu/doc/tlcl.pdf - the first part is a very useful intro :)
    – Zanna
    Mar 14, 2022 at 12:06
0

You can achieve this using find and bash:

find <YOUR SEARCH PATH> -type f -exec bash -c 'filepath="{}"; filename=${filepath##*/}; echo ${filename%.*} >> $filepath' \;

where

  • <YOUR SEARCH PATH> - path to a directory with your files
  • -type f - we are searching for files exactly, not for dirs etc.
  • -exec - executing bash, where the string {} is replaced by the current file name being processed
  • filepath="{}"- create filepath variable with the current full path file name being processed
  • filename=${filepath##*/} - create filename variable extracting filename from full path
  • echo ${filename%.*} >> $filepath - strip last dot with an extension from filename variable and append name to a file being processed using variable filepath

Of course bash command string can be shortened, just tried to explain things better.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .