Use value of variable literally in if statement (bash) [duplicate] - bash

This question already has answers here:
How to manually expand a special variable (ex: ~ tilde) in bash
(19 answers)
Closed 3 years ago.
I need to check if the value of a variable is a path that exists. This is being read from a text file.
Basically, the point I'm stuck at, the line variable is as follows: location_of_folder=~/Desktop/folder\ with\ spaces
I need to check if the path after location_of_folder= exists.
Here's what I've tried:
foo="${line#'location_of_folder='}"
if ! [[ -d "${foo}" ]]
then
echo 'This path exists.'
else
echo 'This path does not exist.'
fi
⁤
if ! [[ -d "${line#'location_of_folder='}" ]]
then
echo 'This path exists.'
else
echo 'This path does not exist.'
fi
However both say the path is nonexistant, which is indeed not true.
And yes, inside of the text file I'm reading from looks like:
location_of_folder=~/Desktop/folder\ with\ spaces
Using bash 3.2.57(1)-release under OSX El Capitan 10.11.6.
Thank you.

This isn't really an answer, but comments are hard to format. There are several issues here, and the sequence below demonstrates some of them. Note that there are blatant bad practices here (do not use eval, but that's essentially what you need if you want to expand that ~ to a path).
$ cat input
location_of_folder=~/Desktop/directory\ with\ spaces
location_of_folder=$HOME/Desktop/directory\ with\ spaces
$ while IFS== read -r name path; do if eval "test -d $path"; then echo "$path" exists; else echo "$path" does not exist; fi; done < input
~/Desktop/directory\ with\ spaces exists
$HOME/Desktop/directory\ with\ spaces exists
$ while IFS== read name path; do if test -d "$path"; then echo "$path" exists; else echo "$path" does not exist; fi; done < input
~/Desktop/directory with spaces does not exist
$HOME/Desktop/directory with spaces does not exist
$ while IFS== read name path; do if eval test -d "$path"; then echo "$path" exists; else echo "$path" does not exist; fi; done < input
bash: test: too many arguments
~/Desktop/directory with spaces does not exist
bash: test: too many arguments
$HOME/Desktop/directory with spaces does not exist
$ while IFS== read name path; do if eval "test -d \"$path\""; then echo "$path" exists; else echo "$path" does not exist; fi; done < input
~/Desktop/directory with spaces does not exist
$HOME/Desktop/directory with spaces exists
Well, this is sort of an answer I guess, since the first line appears to give you what you want. But using eval just to expand ~ is a terrible idea.

Related

Bash script trying to list files unsuccessfully

I'm reading some file paths and names from a text file and trying to test if file exists. I'm not sure what I'm doing wrong but first echo returns filepath and file name whilst the echo inside the if statement doesn't. Any ideas?
#!/bin/bash
while read line; do
echo $line
if [ -f "$line" ]; then
echo "found: $line"
fi
done < /mbackup/temp/images.txt
The only change is adding the -r option to read. That option is documented as:
Backslash does not act as an escape character. The backslash is considered to be part of the line. In particular, a backslash-newline pair may not then be used as a line continuation.
This helps prevent special characters in file names from interfering with your script.
I test this with files containing special characters and it works as you expected.
#!/bin/bash
while read -r line; do
echo $line
if [ -f "$line" ]; then
echo "found: $line"
fi
done < /mbackup/temp/images.txt

read input in bash scrip that is a directory with spaces in the path [duplicate]

This question already has answers here:
How to loop over files in directory and change path and add suffix to filename
(6 answers)
When to wrap quotes around a shell variable?
(5 answers)
Closed 2 years ago.
I can't find a way to put an entry in read that contains spaces? I want to put directories in the "Enter directory to be cleaned:" read. I think it is reading the spaces as separate variables. Input would be something like /home/user/apple sauce The "cleaning" is just removing special characters from filenames.
#!/bin/bash
read -p "Enter directory to be cleaned: " directory
echo "Current Directory Structure"
/usr/bin/ls -la $directory
read input
if [ "$input" == "y" ]
then
echo "Cleaning files..."
for file in $directory; do mv $file $(echo "$file" | sed -e 's/[^A-Za-z0-9._-]/_/g'); done &
else
stop
fi
Another issue I am facing is the cleanup is repeating the entire directory when it creates the new filename. If I run that for file in *; do mv "$file" $(echo "$file" | sed -e 's/[^A-Za-z0-9._-]/_/g'); done & command in the directory itself, it just creates the new filename. If I specify the directory it writes out the whole directory:
++ sed -e 's/[^A-Za-z0-9._-]/_/g'
++ echo '/home/apples/scratch/test1/test:something?'
+ mv '/home/apples/scratch/test1/test:something?' _home_apples_scratch_test1_test_something_
I want it to just change the filename but having issues. Any help is thankful.
I think it is reading the spaces as separate variables
It does not, as you can easily verify with this:
read -p 'Enter string:' x
echo "Entered: >>>$x<<<"
If you dislike quoting your variables (to avoid word splitting), you may consider switching from bash to Zsh. Where you have to write "$x" in bash, you would simply write $x in Zsh.
Hence, you would have to write
for file in "$directory"
but this would loop just one iteration, with file bound to the content of the variable directory. For looping over the entries in this directory, you would do a
for dirent in "$directory"/*

Reading .env with bash (vars with spaces)

I'm using nodedock.
It has a start.sh script to start you docker
#!/usr/bin/env bash
set -e
cd "$( dirname "${BASH_SOURCE[0]}" )"
if [ ! -f .env ]; then
echo "Having .env is required. Maybe you forgot to copy env-example?"
exit 1
fi
while read -r line; do
VARNAME=$(echo ${line} | awk '{sub(/\=.*/,x)}1')
if [[ -z ${!VARNAME} ]]; then
declare -x ${line}
fi
done < <(egrep -v "(^#|^\s|^$)" .env)
docker-compose up -d ${NODEDOCK_SERVICES}
docker-compose logs -t -f ${NODEDOCK_LOG_AFTER_START}
NODEDOCK_SERVICES = nginx node workspace mongo
If found that if you need to have a variable with spaces you have to write your env variable with doubles quotes "nginx node workspace mongo"
The problem is that this "req expression" VARNAME=$(echo ${line} | awk '{sub(/\=.*/,x)}1') doesn't work with double quotes.
Any solution?
The problem is not with your awk expression but when you make a call to the declare built-in. Use proper quotes when declaring it.
declare -x "$str"
because without the quotes, your assignment would look like
declare -x NODEDOCK_SERVICES=nginx node workspace mongo
which splits on white-space and the first word of the resultant string gets assigned to NODEDOCK_SERVICES. But with proper quotes, the assignment would remain intact preserving the spaces in the resultant string.
That said, your whole loop can be modified by making the read loop parse the line with = as de-limiter, so you can easily parse the key/value pairs. At this point it is not clear that the assignments in your file would be of the form 1 or 2 below
NODEDOCK_SERVICES = nginx node workspace mongo
NODEDOCK_SERVICES=nginx node workspace mongo
The below logic would work for both the cases
shopt -s extglob
while IFS== read -r key value; do
key=${key%%+([[:space:]])}
value=${value##+([[:space:]])}
if [[ -z ${!key} ]]; then
declare -x "$key=$value"
fi
done < <(egrep -v "(^#|^\s|^$)" .env)
As a good practice, always quote your variables in bash, unless you see a good reason not to. And lower-casing user defined variables helps you distinguish them from the environment variables maintained by the shell itself.
If you want to read specific variables from an .env file format (maybe not exactly your question but it might help others as your title might be misleading):
read_var() {
VAR=$(grep "^$1=" $2 | xargs)
IFS="=" read -ra VAR <<< "$VAR"
IFS=" "
echo ${VAR[1]}
}

Shell Script check if file exists, and has read permissions

In my shell script i'm trying to check if a specific file is exists and if it has reading permissions.
My file's path has spaces in it.
I quoted the file path:
file='/my/path/with\ some\ \spaces/file.txt'
This is the function to check if the file exists:
#Check if file exists and is readable
checkIfFileExists() {
#Check if file exists
if ! [ -e $1 ]; then
error "$1 does not exists";
fi
#Check if file permissions allow reading
if ! [ -r $1 ]; then
error "$1 does not allow reading, please set the file permissions";
fi
}
Here I double quote to make sure it gets the file as a one argument:
checkIfFileExists "'$file'";
And I receive an error from the bash saying:
[: too many arguments
Which make me thinks it doesn't get it as a one argument.
But in my custom error, I do get the whole path, and it says it doesn't exists.
Error: '/my/path/with\ some\ \spaces/file.txt' does not exists
Although it does exists, and when I tried to read it with "cat $file" I get a permission error..
what am I'm doing wrong?
The proper way to quote when you require variable interpolation is with double quotes:
if [ -e "$1" ]; then
You need similar quoting throughout the script, and the caller needs to quote or escape the string -- but not both. When you assign it, use one of these:
file='/my/path/with some spaces/file.txt'
# or
file=/my/path/with\ some\ spaces/file.txt
# or
file="/my/path/with some spaces/file.txt"
then use double quotes around the value to pass it in as a single argument:
checkIfFileExists "$file"
Again, where you need the variable's value to be interpolated, use double quotes.
For a quick illustration of what these quotes do, try this:
vnix$ printf '<<%s>>\n' "foo bar" "'baz quux'" '"ick poo"' \"ick poo\" ick\ poo
<<foo bar>>
<<'baz quux'>>
<<"ick poo">>
<<"ick>>
<<poo">>
<<ick poo>>
Furthermore, see also When to wrap quotes around a shell variable?
if [[ -e $1 ]];then
echo it exists
else
echo it doesnt
fi
if [[ -r $1 ]];then
echo readable
else
echo not readable
fi

Escaping spaces in path names

I am trying to escape spaces in a path name /Volumes/NO NAME. I need to access files present within this directory via a bash script. I have used sed and printf commands and neither seems to have worked. The code I have so far:
while read line
do
if [[ -d ${line} ]]; then
echo "${line} is a directory. Skipping this.";
elif [[ -f ${line} ]]; then
spacedOutLine=`echo ${line} | sed -e 's/ /\\ /g'`;
#spacedOutLine=$(printf %q "$line");
echo "line = ${line} and spacedOutLine = ${spacedOutLine}";
else
echo "Some other type of file. Unrecognized.";
fi
done < ${2}
Neither of this seems to have worked. For an input like: /Volumes/NO NAME/hello.txt, the output is:
/Volumes/NO: No such file or directory
NAME/hello.txt: No such file or directory
I am on a Mac and using bash via the terminal. I have also gone through a lot of other posts on SO about this which have not helped me.
Use double quotes while calling the ${line} variable.
"${line}"

Resources