BASH "for ... in ..." don't work with variables [duplicate] - linux

This question already has an answer here:
In bash, how do I expand a wildcard while it's inside double quotes?
(1 answer)
Closed 2 years ago.
I want to write a simple script that does something for every file in user-defined directory. Here's a script that works for predefined directory:
for file in mydir/*; do
printf "$file"
done
Here's similar script that prints name of each file in the directory defined by variable:
for file in "$nicedir*"; do
printf "$file"
done
This second script don't work. Of course, I remembered about slash at the end of the path. (I passed ./ as the argument instead of just .)

Pathname expansion doesn't happen in quoted strings. Keep the wildcard outside of the quotes:
for file in "$nicedir"* ; do
printf '%s\n' "$file"
done
The final slash is usually not required in paths, so you'll more often see
for file in "$nicedir/"*
# or equivalent
for file in "$nicedir"/*

Related

How do I pass quoted shell variables as arguments correctly? [duplicate]

This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Variable containing multiple args with quotes in Bash
(4 answers)
Closed 10 months ago.
As part of a build pipeline I have a shell script that zips up a directory. I want to use a variable to define which patterns should be ignored.
I'm doing something like this:
IGNORE='"*.md" "some-folder/*"'
zip -x $IGNORE -r my-zip.zip ./*
Which doesn't appear to work, the ignored files and folders are still included in the zip archive.
This does work if I create the command and then pipe it through to sh though, so I'm confident that the variable contains the correct values:
echo "zip -x $IGNORE -r my-zip.zip ./*" | sh
I think it might be something to do with the quotes, since this works as expected without them. However this fails as soon as I attempt to add more than 1 pattern to IGNORE.
IGNORE=*.md
zip -x $IGNORE -r my-zip.zip ./*
What am I missing in order to be able to pass these patterns correctly quoted?
Edit: this does also not appear to work as an array, as suggested by this question.
IGNORE='"*.md" "some-folder/*"'
EXCLUDE=($IGNORE)
zip -x ${EXCLUDE[#]} -r my-zip.zip ./*

Batch remove extra file extensions in bash [duplicate]

This question already has answers here:
How can I remove the extension of a filename in a shell script?
(15 answers)
bash - command substitution is omitting whitespaces
(1 answer)
Closed 12 months ago.
I converted some files to another format but in doing so added an extra extension. For example foo.bar.temp. I wrote a script to delete the .temp, but it doesn't work when the filenames have spaces.
for f in *; do mv "$f" $(basename "$f" .temp) ; done
If I double escape "'$f'" then basename won't read the extension. If I leave it as is then it will think that the second word in the title is the directory I want to move to.
How can I just remove the .temp?

star wildcard in bash [duplicate]

This question already has answers here:
Rename multiple files in shell [duplicate]
(4 answers)
Closed 4 years ago.
I've got a small problem with my bash script. I try to change file name in current directory for whole files with txt extension to text extension. For exampel 1.txt to 1.text
My script looks like this now:
#!/bin/bash
FILES=`ls /home/name/*.txt`
NAME=*.txt
RENAME=*.text
for file in FILES
do
mv $NAME $RENAME
done
i try whole combination with single, double quotes and backticks and I receive errors all the time.
Do you have some ideas how to receive wildcards "*" in bash?
Thanks.
That's not at all how you do that.
#!/bin/bash
shopt -s nullglob
OLD=.txt
NEW=.text
FILES=(/home/name/*"$OLD")
for file in "${FILES[#]}"
do
mv "$file" "${file%$OLD}$NEW}"
done
There are a number of issues with your script. Firstly, you shouldn't run ls and attempt to store its output like that. If you want to iterate through those file, just do it in the loop:
for file in /home/name/*.txt
Now the shell is doing all the work for you, and as a bonus handling any kind of weird filenames that you might have.
In your example you were looping over the literal string "FILES", not the variable, but I guess that was just a typo.
The built-in way to change the filename is to use a parameter expansion to remove the old one, then concatenate with the new one:
old=txt
new=text
for file in /home/name/*"$old"
do
mv "$file" "${file%$old}$new"
done
If it is possible that there are no files matching the glob, then by default, the /home/name/*.txt will not be expanded and your loop will just run once. then you have a couple of options:
use shopt -s nullglob so that /home/name/*.txt expands to null, and the loop is skipped
add an explicit check inside the loop to ensure that $file exists before trying to mv:
for file in /home/name/*"$old"
do
[ -e "$file" ] || continue
mv "$file" "${file%$old}$new"
done
You can use rename to rename filenames.
rename .txt .text /home/name/*.txt
And if you want to do this by looping, you can
for FILE in /data/tmp/*.txt; do
mv "${FILE}" "${FILE/.txt/.text}"
done

Bash script doesn't evaluate variable in filename [duplicate]

This question already has answers here:
What is the difference between ${var}, "$var", and "${var}" in the Bash shell?
(7 answers)
Closed 5 years ago.
I have a bash script which creates a backup of my folder. It should name the tar gz file using a version number but it doesn't:
#!/bin/bash
ver='1.2.3'
tar czf $ver_myfolder.tar.gz .
Expected output:
1.2.3_myfolder.tar.gz
Actual output:
_myfolder.tar.gz
If I append the variable like this:
#!/bin/bash
ver='1.2.3'
tar czf myfolder-$ver.tar.gz .
It works
You should use ${var} here since you are appending underscore after it which is considered a valid character for variable names. Due to that fact shell doesn't know whether you're expanding variable name $ver or $ver_myfolder:
ver='1.2.3'
tar czf "${ver}_myfolder.tar.gz" .
Since $ver_myfolder is unset, you get an empty value.
Because the underscore is a valid character for a variable name, you should use braces to explicitly specify the range of your variable:
${ver}_myfolder.tar.gz
^ ^
Without braces, Bash will actually try to parse
${ver_myfolder}.tar.gz
For your edited question, it is because the dot is not a valid character for a variable name, so Bash will not attempt to parse the dot into the name lookup. Even if you put it into braces, a variable name containing a dot is still invalid:
$ echo ${ver.}
bash: ${ver.}: bad substitution
$ ver.=1.2.3
ver.=1.2.3: command not found

Bash script can't find file in relative path directory [duplicate]

This question already has answers here:
When to wrap quotes around a shell variable?
(5 answers)
Closed 1 year ago.
I'm new to bash and I am trying to create a script that should find an archive in a given directory. $1 is the name of archive.
When the given path is ./1/ar.tgz the script works. But when path is ../data 1/01_text.tgz I have the following problem:
dirname: extra operand "1/01_text.tgz"
and then No such file or directory.
Here is my code fragment:
VAR=$1
DIR=$(dirname ${VAR})
cd $DIR
What am I doing wrong?
Ahmed's answer is right, but you also need to enclose VAR in double quotes. The correct code fragment is:
VAR=$1
DIR=$(dirname "$VAR")
cd "$DIR"
The space is causing the problem: cd $DIR gets expanded to cd ../data 1/01_text.tgz and cd doesn't know what to make of the third "argument". Add quotes around the directory: cd "$DIR".

Resources