Linux rename multiple files - linux

I have some files named in a specifc pattern, for example, ab_2000_1.jpg. In this name 2000 is representing years and 1 representing month(1 means january). I have a 20 years of monthly files like this.
Now I want to rename every one of them into the following format ab_2000_1_12.jpg, ab_2000_2_12.jpg, etc
I know how to rename files using rename and sed command. But I want to know how can I loop this command for all files.
Any help is highly appreciated.

You can use a for loop to loop over all file names matching a pattern as for file in pattern; do some_commands; done.
You don't need sed to modify the file name in this case. A variable substitution like ${variable%pattern} will remove the shortest string matching pattern from the end of the variable value.
The following example code will remove .jpg from the end of the file name and append _12.jpg to the result.
for file in ab_*_*.jpg
do
mv "$file" "${file%.jpg}_12.jpg"
done

Related

Sed removing last 4 characters in a file text inside a folder

This is a linux problem
So I need to make a folder called "Notes", and inside that folder I need to make 6 files. The files are named with ID number, as follows:
a00144998.txt
a00154667.txt
a00130933.txt
a00143561.txt
a00157888.txt
The first 3 letter are always "a00", and the 4th & 5th letter are the year. So I now need to copy the ID number/title of file without the '.txt' extension, and paste it on a new file outside the folder "Notes"
Example file a00144998, it shows year 14(from the 4th & 5th letter), so I will copy a00144998 to a new file named "year14.txt" and sort it. Same as a00154667, it shows year 15, so I will copy a00154667 to a new file named "year15.txt". So at the end, file "year14.txt" will have :
a00143561
a00144998
I have found the code, and it works if the files are not in the folder. But once I create files inside folder, this code doesn't work, it keeps copying the txt extension. Any idea? Thanks!
ls ~/Notes/???14????.txt|sed 's/.\[4\]$//'|sort>year14.txt
No need for sed, you can do it all in a bash oneliner:
for f in a0014*.txt; do echo ${f:5:4}; done | sort > year14.txt
This will loop over every file matching the glob a0014*.txt and put each string in an f variable, echo out 4 characters starting after the 5th character.
TLDP has a great guide on string manipulation in bash.
You should also avoid parsing ls. It's meant to be human and not machine readable.

copy and append specific lines to a file with specific name format?

I am copying some specific lines from one file to another.
grep '^stringmatch' /path/sfile-*.cfg >> /path/nfile-*.cfg
Here what's happening: its creating a new file called nfile-*.cfg and copying those lines in that. The file names sfile- * and nfile- * are randomly generated and are generally followed by a number. Both sfile-* and nfile-* are existing files and there is only one such file in the same directory. Only the number that follows is randomly generated. The numbers following in sfile and nfile need not be same. The files are not created simultaneously but are generated when a specific command is given. But some lines from one file to the another file needs to be appended.
I'm guessing you actually want something like
for f in /path/sfile-*.cfg; do
grep '^stringmatch' "$f" >"/path/nfile-${f#/path/sfile-}"
done
This will loop over all sfile matches and create an nfile target file with the same number after the dash as the corresponding source sfile. (The parameter substitution ${variable#prefix} returns the value of variable with any leading match on the pattern prefix removed.)
If there is only one matching file, the loop will only run once. If there are no matches on the wildcard, the loop will still run once unless you enable nullglob, which changes the shell's globbing behavior so that wildcards with no matches expand into nothing, instead of to the wildcard expression itself. If you don't want to enable nullglob, a common workaround is to add this inside the loop, before the grep;
test -e "$f" || break
If you want the loop to only process the first match if there are several, add break on a line by itself before the done.
If I interpret your question correctly, you want to output to an existing nfile, which has a random number in it, but instead the shell is creating a file with an asterisk in it, so literally nfile-*.cfg.
This is happening because the nfile doesn't exist when you first run the command. If the file doesn't exist, bash will fail to expand nfile-*.cfg and will instead use the * as a literal character. This is correct behaviour in bash.
So, it looks like the problem is that the nfile doesn't exist when you start your grep. You'll need to create one.
I'll leave code to others, but I hope the explanation is useful.

Paste header line in multiple tsv (tab separated) files

I have multiple .tsv files named as choochoo1.tsv, choochoo2.tsv, ... choochoo(nth).tsv files. I also have a main.tsv file. I want to extract the header line in main.tsv and paste over all choochoo(nth).tsv files. Please note that there are other .tsv files in the directory that I don't want to change or paste header, so I can't do *.tsv and select all the .tsv files (so need to select choochoo string for wanted files). This is what I have tried using bash script, but could not make it work. Please suggest the right way to do it.
for x in *choochoo; do
head -n1 main.tsv > $x
done
You have a problem with the file glob, as well as the redirect:
the file glob will catch things like AAchoochoo but not choochoo1.tsv and not even AAchoochoo.tsv
the redirect will overwrite the existing files instead of adding to them. The redirect command for adding to a file is >>, but that will append text to the end and you want to prepend text in the beginning.
The problem with prepending text to an existing file, is that you have to open the file for both reading and writing and then stream both prepended text and original text, in order - and that is usually where people fail because the shell can't open files like that (there is a slightly more complex way of doing this directly, by opening the file for both reading and writing, but I'm not going to address that further).
You might want to use a temporary file, something like this:
for x in choochoo[0-9]*.tsv; do
mv "$x"{,.orig}
(head -n1 main.tsv; cat "$x.orig") > $x
rm "$x.orig"
done

How to remove part of file names between periods?

I would like to rename many files in this format
abc.123.fits
abcd.1234.fits
efg.12.fits
to this format
abc.fits
abcd.fits
efg.fits
I tried the rename function, but since the part I'm trying to replace is not the same in all files, it did not work. I am using Linux.
for f in *; do mv "$f" "${f%%.*}.${f##*.}"; done`
${f%%.*} removes everything after the first period, including the period. ${f##*.} removes everything before the last period, including the period (i.e. it gets the file extension). Concatenating these two, with a period between them, gives you the desired result.
You can change the * to a more restrictive pattern such as *.fits if you don't want to rename all files in the current directory. The quotes around the parameters to mv are necessary if any filenames contain whitespace.
Many other variable substitution expressions are available in bash; see a reference such as TLDP's Bash Parameter Substitution for more information.

Find required files by pattern and the change the pattern on Linux

I need to find all *.xml files that matched by pattern on Linux. I need to have written the file name on the screen and then change the pattern in the file just was found.
For instance.
I can start the script with arguments for keyword and for value, i.e
script.sh keyword "another word"
Script should find all files with keyword and do the following changes in the files containing keyword.
<keyword></keyword> should be the same <keyword></keyword>
<keyword>some word</keyword> should be like this <keyword>some word, another word</keyword>
In other words if initially value in keyword node was empty, then I don't need to change it and if it contains some value then I need to extend it with the value I will specify.
What is best way to do this on Linux? Using find, grep, sed?
Performance is also important since the number of files are thousands.
Thank you.
It seems using a combination of find, grep and sed would do this and they are pretty fast since you'll be doing text processing so there might not be a need for xml processing but if you could you give an example or rephrase your question I might be able to provide more help.

Resources