Bash loop for multiple files [duplicate] - bash

This question already has answers here:
Bash input for multiple file
(5 answers)
Closed 8 years ago.
I have thousands of two set of files, one with name.ext and another set for the same name ending with name.new.psl. So for every name.ext there is a name.new.psl. Now I have to pass this as arguments to a script such as customise.pl name.ext name.new.psl
Any ideas for a loop in bash? The first name is common for each name.ext and name.new.psl like:
perl customise.pl name.ext name.new.psl

for f in *.ext ; do
perl customise.pl "${f}" "${f/%.txt/.new.psl}"
done
Will do it for you in the current working directory.

for fname in *.ext
do
perl customise.pl "$fname" "${fname%.ext}.new.psl"
done
The above does not require any special bash features. So, it is compatible with, for example, dash which is the default shell (/bin/sh) on debian-derived distributions.
The trick above is that ${fname%.ext} tells the shell to remove the text .ext from the end of $fname, leaving just the "name" part. Thus, "${fname%.ext}.new.psl" removes .ext adds the .new.psl extension.
The file names in the code above are in double-quotes. This is so that this script will work even if the file names have spaces in them.

for i in `ls *.ext`; do NAME=`echo $i | awk -F '.' '{print $1}'`; perl customise.pl $NAME.ext $NAME.new.psl; done

Related

Cannot echo bash variable from single line command [duplicate]

This question already has answers here:
Rename multiple files, but only rename part of the filename in Bash
(6 answers)
Closed 4 years ago.
I have the following file names where I am trying to relabel v5.4b to v5.7:
v5.4b_lvl-1.e8974326
v5.4b_lvl-1.o8974326
v5.4b_lvl-1.pe8974326
v5.4b_lvl-1.po8974326
v5.4b_lvl-2.1.e8974303
v5.4b_lvl-2.1.o8974303
v5.4b_lvl-2.1.pe8974303
v5.4b_lvl-2.1.po8974303
v5.4b_lvl-2.2.e8974304
v5.4b_lvl-2.2.o8974304
v5.4b_lvl-2.2.pe8974304
v5.4b_lvl-2.2.po8974304
v5.4b_lvl-3.1.e8974305
v5.4b_lvl-3.1.o8974305
v5.4b_lvl-3.1.pe8974305
v5.4b_lvl-3.1.po8974305
v5.4b_lvl-4.1.e8974327
v5.4b_lvl-4.1.o8974327
v5.4b_lvl-4.1.pe8974327
v5.4b_lvl-4.1.po8974327
I can't do mv v5.4b_* v5.7_* because it thinks v5.7_* is a directory so I am trying a for-loop but I can't get it to work
I am trying the recommended answer from this SO post How to set a variable to the output of a command in Bash? but getting a bunch of empty lines.
What am I doing incorrectly? How can I save the output of cut to SUFFIX so I can mv $i v5.7_$SUFFIX?
-bash-4.1$ for i in v5.4b*; do echo $i | SUFFIX=`cut -f2 -d'_'`; echo ${SUFFIX}; done
You've got echo $i in the wrong place. The output of that command needs to be piped to cut for it to read anything, then the result is assigned to SUFFIX:
for i in v5.4b*
do
SUFFIX=`echo $i | cut -f2 -d'_'`
echo ${SUFFIX}
done
If you rename utility then just do:
rename -n 's/v5\4.b/v5.7/' v5.4b*
PS: -n is for dry-run. You may remove it later for real renaming.
If rename is not available then use:
for i in v5.4b*; do
echo mv "$i" "${i/v5.4b/v5.7}"
done
Remove 'echo` if you're satisfied with the output.

Loop through all the files with .txt extension in bash [duplicate]

This question already has answers here:
Loop through all the files with a specific extension
(7 answers)
Closed 4 years ago.
I am trying to loop over files in a folder and test for .txt extensions.
But I get the following error: "awk: cannot open = (No such file or directory)
Here's my code:
!/bin/bash
files=$(ls);
for file in $files
do
# extension=$($file | awk -F . '{ print $NF }');
if [ $file | awk -F . "{ print $NF }" = txt ]
then
echo $file;
else
echo "Not a .txt file";
fi;
done;
The way you are doing this is wrong in many ways.
You should never parse output of ls. It does not handle the filename containing special characters intuitively See Why you shouldn't parse the output of ls(1)
Don't use variables to store multi-line data. The output of ls in a variable is expected to undergo word splitting. In your case files is being referenced as a plain variable, and without a delimiter set, you can't go through the multiple files stored.
Using awk is absolutely unnecessary here, the part $file | awk -F . "{ print $NF }" = txt is totally wrong, you are not passing the name the file to the pipe, just the variable $file, it should have been echo "$file"
The right interpreter she-bang should have been set as #!/bin/bash in your script if you were planning to run it as an executable, i.e. ./script.sh. The more recommended way would be to say #!/usr/bin/env bash to let the shell identify the default version of the bash installed.
As such your requirement could be simply reduced to
for file in *.txt; do
[ -f "$file" ] || continue
echo "$file"
done
This is a simple example using a glob pattern using *.txt which does pathname expansion on the all the files ending with the txt format. Before the loop is processed, the glob is expanded as the list of files i.e. assuming the folder has files as 1.txt, 2.txt and foo.txt, the loop is generated to
for file in 1.txt 2.txt foo.txt; do
Even in the presence of no files, i.e. when the glob matches empty (no text files found), the condition [ -f "$file" ] || continue would ensure the loop is exit gracefully by checking if the glob returned any valid file results or just an un-expanded string. The condition [ -f "$file" ] would fail for everything if except a valid file argument.
Or if you are targeting scripts for bourne again shell, enable glob options to remove non-matching globs, rather than preserving them
shopt -s nullglob
for file in *.txt; do
echo "$file"
done
Another way using shell array to store the glob results and parse them over later to do a specific action on them. This way is useful when doing a list of files as an argument list to another command. Using a proper quoted expansion "${filesList[#]}" will preserve the spacing/tabs/newlines and other meta characters in filenames.
shopt -s nullglob
filesList=(*.txt)
for file in "${filesList[#]}"; do
echo "$file"
done

Cut sub-string and rename files use shell command [duplicate]

This question already has answers here:
How to rename with prefix/suffix?
(10 answers)
Closed 7 years ago.
How to rename file names? I want to map names:
abc-hdpi.png ⟶ abc.png
bcd-hdpi.png ⟶ bcd.png
...
I have many files to do this, so mv abc-hdpi.png abc.png is not a good solution.
Search for prename (Perl rename) command; it can do the job easily:
prename 's/-hdpi.png/.png/' *-hdpi.png
Failing that:
for file in *-hdpi.png
do
mv "$file" "${file%-hdpi.png}.png"
done
You can extract from a fixed length string like so:
NAME[0]="abc-hdpi.png"
NAME[1]="def-hdpi.png"
NAME[2]="ghi-hdpi.png"
NAME[3]="jkl-hdpi.png"
NAME[4]="mno-hdpi.png"
rename(){
var=$1
mv $var $var{0:2}$var{8:}
}
for i in "${Name[#]}"
do
rename $i
done

bash script rename multiple files [duplicate]

This question already has answers here:
Rename filename to another name
(3 answers)
Closed 7 years ago.
Let´s say I have a bunch of files named something like this: bsdsa120226.nai bdeqa140223.nai and I want to rename them to 120226.nai 140223.nai. How can i achieve this using the script below?
#!/bin/bash
name1=`ls *nai*`
names=`ls *nai*| grep -Po '(?<=.{5}).+'`
for i in $name1
do
for y in $names
do
mv $i $y
done
done
Solution:
name1=`ls *nai*`
for i in $name1
do
y=$(echo "$i" | grep -Po '(?<=.{5}).+')
mv $i $y
done
This:
#!/bin/bash
shopt -s extglob nullglob
for file in *+([[:digit:]]).nai; do
echo mv -nv -- "$file" "${file##+([^[:digit:]])}"
done
Remove the echo if you're happy with the mv commands.
Note. This solution does not assume that there are 5 leading characters to delete. It will delete all the leading non-numeric characters.
Using only bash, you could do this:
for file in *nai* ; do
echo mv -- "$file" "${file:5}"
done
(Remove the echo when satisfied with the output.)
Avoid ls in scripts, except for displaying information. Use plain globbing instead.
See also How do I do string manipulations in bash? for more string manipulation techniques.
Your script can't work with that structure: if you have 5 files, it will call mv five times for the first file (once for each element in the second list), five times for the second, etc. You'd need to iterate over the two sets of names in lockstep. (It also doesn't deal with things like whitespace in filenames.)
You would be better off using rename (prename on some systems) since that allows you to use Perl regular expressions to do the renaming, along the lines of:
prename 's/^.{5}//' *.nai
The reason your script is not behaving is that, for every source file, you're attempting to rename it to every target file.
If you need to limit yourself to using that script, you need to work out the single target file for each source file, something like:
#!/bin/bash
for i in *.nai; do
y=$(echo "$i" | cut -c6-)
mv "$i" "$y"
done
If your system has rename tool, it's better to go with the simple rename command,
rename 's/^.{5}//' *.nai
It just remove the first 5 characters from the file name.
OR
for i in *.nai; do mv "$i" $(grep -oP '(?<=^.{5}).+' <<< "$i"); done

Shell script - loop through file names and extract numbers in the filename

Using a linux shell script, I am trying to loop through all of the file names in a directory and extract the numbers out of the file name before I process the file.
Something like this:
for files in `ls *.gz`
do
echo "Looking at... $files"
gunzip $files
echo "$files" | awk '/[0-9]/' ' {print $1}'
echo "$files is unzipped"
done
Thank you for any help with this.
You can substitute all non-numbers in the file name.
echo "${files//[!0-9]/}"
This will obviously produce concatenated numbers if a file name contains multiple runs of digits. For example, 12a34.gz gets turned into 1234.
This substitution mechanism is a Bash-only feature, and not supported by plain sh.

Resources