This question already has answers here:
How to manually expand a special variable (ex: ~ tilde) in bash
(19 answers)
Tilde expansion in quotes
(3 answers)
Closed 4 years ago.
I'm not sure if this question has been asked or answered. If it has, I apologize. I wasn't too sure what to look for, but I'm writing a bash script to take in a path from the user, my script works with absolute and relative paths. But I was wondering if there was a way to interpret the shorthand notation to these paths. For example ~/ for home or . for current or ../ for a directory up. I was having a bit of trouble figuring this out. Here is my current bash script for reference.
#!/bin/bash
echo "What is the path you'd like me to search?"
read PASSED
while [ ! -d $PASSED ]; do
echo "$PASSED is not a valid directory"
echo "Please provide a valid Path"
read PASSED
done
ls -l $PASSED | awk -v user=`whoami` '{ if($3 == user){print $0}}'
. and .. are not shortcuts, they're actual directory names that exist in every directory. You don't need to do anything to support this.
~, however, is bash syntax that you can emulate.
In the simplest case, just check for a leading ~ and replace:
# Read some path
printf 'Enter a path: '
IFS='' read -r mypath
# If the path starts with ~, replace it with $HOME
[[ $mypath = '~'* ]] && mypath="$HOME${mypath#'~'}"
# Show what we got
echo "The path is $mypath, aka $(realpath "$mypath")"
Note that this does not support the various other ~ syntaxes like ~username or ~+
Related
This question already has answers here:
Pattern match does not work in bash script
(3 answers)
Closed 1 year ago.
The following script(s) work fine if copy and pasted into the shell. However, when creating a script file and interpreting with BASH- it fails with error: "Syntax error:"(" unexpected. It appears the logical "!" (not) is not being interpreted.
'''
#!/bin/bash
for TARGETFOLDER in !(*Sidereal);
do cd $TARGETFOLDER;
echo "--->---> Processing" $TARGETFOLDER;
for FILTERFOLDER in *;
do cd $FILTERFOLDER;
echo "--->--->---> Processing" $TARGETFOLDER $FILTERFOLDER;
pp_prepare -keep_wcs *r.fits;
pp_photometry -snr 3 -minarea 12 -aprad 6 *r.fits;
pp_calibrate -instrumental *r.fits;
cd ..;
done;
cd ..;
done;
'''
Is there a way to have this interpreted correctly- or maybe rewritten to conform to an interpreter? The original script as given was not formatted nicely... sorry
thanks
You need to explicitly enable extended glob support.
shopt -s extglob
for TARGETFOLDER in !(*Sidereal);
...
This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 3 years ago.
I'm trying to pass a dynamic command that executes ls as a string that lists the files of a directory that contains spaces. However, my ls command always interprets my one directory containing spaces as multiple directories no matter what I do.
Consider the following simplified version of my shell script:
#!/bin/sh
export WORK_DIR="/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work"
echo "WORK_DIR=$WORK_DIR"
export LS_CMD="ls -A \"$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs\""
echo "LS_CMD=$LS_CMD"
if [ -n "$($LS_CMD)" ]
then
echo "### Removing all logs"
sudo rm "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs/*"
else
echo "### Not removing all logs"
fi
This script results in the following output:
WORK_DIR=/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work
LS_CMD=ls -A "/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work/dependencies/apache-tomcat-8.0.45/logs"
ls: "/Users/jthoms/Dropbox: No such file or directory
ls: (My: No such file or directory
ls: Company)/backup-jthoms/Work/dependencies/apache-tomcat-8.0.45/logs": No such file or directory
### Not removing all logs
How can I correctly escape my shell variables so that the ls command interprets my directory as a single directory containing spaces instead of multiple directories?
I recently changed this script which used to work fine for directories containing no spaces but now doesn't work for this new case. I'm working on Bash on MacOSX. I have tried various forms of escaping, various Google searches and searching for similar questions here on SO but to no avail. Please help.
Variables are for data. Functions are for code.
# There's no apparent need to export this shell variable.
WORK_DIR="/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work"
echo "WORK_DIR=$WORK_DIR"
ls_cmd () {
ls -A "$1"/dependencies/apache-tomcat-8.0.45/logs
}
if [ -n "$(ls_cmd "$WORK_DIR")" ]; then
then
echo "### Removing all logs"
sudo rm "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs/"*
else
echo "### Not removing all logs"
fi
However, you don't need ls for this at all (and in general, you should avoid parsing the output of ls). For example,
find "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs/" -type f -exec rm -rf {} +
You could use
# ...
if [ -n "$(eval "$LS_CMD")" ]
# ...
See http://mywiki.wooledge.org/BashFAQ/050
Or even
# ...
if [ -n "$(bash -c "$LS_CMD")" ]
# ...
But you are probably better off using a dedicated function and/or even something more specific to your problem (using find instead of ls is usually a good idea in these cases, see some examples in the answers for this question).
Use arrays, not strings, to store commands:
ls_cmd=(ls -A "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs")
echo "ls_cmd=${ls_cmd[*]}"
if [ -n "$("${ls_cmd[#]}")" ]; then …
(The syntax highlighting on the last line is incorrect and misleading: we’re not unquoting ${ls_cmd[#]}; in reality, we are using nested quotes via a subshell here.)
That way, word splitting won’t interfere with your command.
Note that you can’t export arrays. But you don’t need to, in your code.
As others have noted, it’s often a better idea to use functions here. More importantly, you shouldn’t parse the output of ls. There are always better alternatives.
This question already has answers here:
How to check if a files exists in a specific directory in a bash script?
(3 answers)
Closed 4 years ago.
I'm not sure how to word my question exactly...
I have the code
if grep "mynamefolder" /vol/Homefs/
then
echo "yup"
else
echo "nope"
fi
which gives me the output
grep: /vol/Homefs/: Is a directory
nope
The sh file containing the code and the directory I'm targeting are not in the same directory (if that makes sense).
I want to find the words myfoldername inside /vol/Homefs/ without going through any subdirectories. Doing grep -d skip, which I hoped would "skip" subdirectories and focus only directories, just gives me nope even though the folder/file/word I'm testing it on does exist.
Edit: I forgot to mention that I would also like mynamefolder to be a variable that I can write in putty, something like
./file spaing and spaing being the replacement for myfoldername.
I'm not sure if I did good enough explaining, let me know!
You just want
if [ -e /vol/Homefs/"$1" ]; then
echo yup
else
echo nope
fi
The [ command, with the -e operator, tests if the named file entry exists.
vim is not involved, and grep is not needed.
If you're insisting on using grep, you should know grep doesn't work on directories. You can convert the directory listing to a string.
echo /vol/Homefs/* | grep mynamefolder
This question already has answers here:
Forcing bash to expand variables in a string loaded from a file
(13 answers)
Closed 7 years ago.
Let's say I have a file called path.txt containing the text $HOME/filedump/ on a single line. How can I then read the contents of path.txt into a variable, while having Bash parse said content?
Here's an example of what I'm trying to do:
#!/bin/bash
targetfile="path.txt"
target=$( [[ -f $targetfile ]] && echo $( < $targetfile ) || echo "Not set" )
echo $target
Desired output: /home/joe/filedump/
Actual output: $HOME/filedump/
I've tried using cat in place of <, wrapping it in quotes, and more. Nothing seems to get me anywhere.
I'm sure I'm missing something obvious, and there's probably a simple builtin command. All I can find on Google is pages about reading variables from ini/config files or splitting one string into multiple variables.
If you want to evaluate the contents of path.txt and assign that to target, then use:
target=$(eval echo $(<path.txt))
for example:
$ target=$(eval echo $(<path.txt)); echo "$target"
/home/david/filedump/
This might not necessarily suit your needs (depending on the context of the code you provided), but the following worked for me:
targetfile="path.txt"
target=$(cat $targetfile)
echo $target
Here's a safer alternative than eval. In general, you should not be using configuration files that require bash to evaluate their contents; that just opens a security risk in your script. Instead, detect if there is something that requires evaluation, and handle it explicitly. For example,
IFS= read -r path < path.txt
if [[ $path =~ '$HOME' ]]; then
target=$HOME/${path#\$HOME}
# more generally, target=${path/\$HOME/$HOME}, but
# when does $HOME ever appear in the *middle* of a path?
else
target=$path
fi
This requires you to know ahead of time what variables might appear in path.txt, but that's a good thing. You should not be evaluating unknown code.
Note that you can use any placeholder instead of a variable in this case; %h/filedump can be detected and processed just as easily as $HOME/filedump, without the presumption that the contents can or should be evaluated as shell code.
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