I am working on a Mac. I have a directory called 1. A (with a white space in the name) inside the directory Test
Now, the following ksh script
typeset MyPath=1.*
print $MyPath
cd $MyPath
pwd
cd ..
touch $MyPath/File.txt
produces
1. A
Test/1. A
touch: 1.*/File.txt: No such file or directory
Thus it looks like both print and cd recognize the pattern "1. A" but touch does not. Why?
Always quote your variables unless you want the expansion to be split on spaces and wilcards to be expanded.
MyPath=$(IFS= echo 1.*)
print "$MyPath"
cd "$MyPath"
pwd
cd ..
touch "$MyPath/File.txt"
echo 1.* expands the wildcard. The assignment IFS= disables word splitting by clearing the field separator variable (this is needed in case the name has multiple consecutive spaces, otherwise they would be merged).
Related
What is the difference between these two commands to find the user home-
$(eval echo ~<username>)
echo ~/
are there any scenario when both return different results?
Tilde expansion is done by the shell before executing the command. So in both cases, the home directory becomes the argument to echo.
eval re-evaluates its arguments. So if the home directory contains any characters that have special meaning to the shell, these will be interpreted. For instance, if the user's home directory were /home/$foo,
echo ~username
would display the pathname with the literal $foo in it, but
eval echo ~username
would replace $foo with the value of the foo variable.
Next, putting $() around a command means that the output of the command is substituted into the command line, and then the command line is executed. So
$(echo ~username)
$(eval echo ~username)
will both try to execute the home directory as a command, which will get an error because directories aren't executable programs. But if you meant that this is being used as an argument, e.g.
cd $(echo ~username)
vs
cd ~username
There should be little difference. The only difference would be if the home directory pathname contains whitespace or wildcard characters, because these are processed after $() substitution. This problem can be avoided by quoting:
cd "$(echo ~username)"
In tcsh I can extract second path element from the end of path by following way
cd /some/long/directory/structure/path/
set x=`pwd`
echo ${x:h:h:t}
directory
How can I do the same in bash?
I mean , does bash also have this kind of modifiers?
The csh-style modifiers can be used with history expansion (unsurprisingly, because history expansion was borrowed from csh).
$ cd /some/long/directory/structure/path/
$ echo !!:1:h:h:t
echo directory
directory
!!:1 selects word 1 (counting from zero) of the previous command, so the argument to cd.
(echo directory appears on standard error because the shell defaults to displaying the result of history expansion before actually executing the resulting command.)
In a non-interactive bash script, history expansion commands as in #chepner's answer won't normally be available. However, you do have parameter expansions like:
$ cd /some/long//directory///structure/path/
$ set x=$(pwd)
$ echo $x
/some/long/directory/structure/path
$ y=${y%/*/*} # each /* is equivalent to one :h
$ y=${y##*/} # equivalent to :t
$ echo $y
directory
cd /some/long/path/somewhere
x=$PWD
basename "$(dirname "$x")"
> path
dirname gets the absolute path of the parent folder of the argument. basename gets the name of the argument.
Edit: remembered the much better way than I was doing before.
I'm coding up a sort of custom rm script that I would like to pass wildcard matches to. I have several files in the working directory that would match the wildcard that I'm passing to the script, but I'm only getting one of them back from a simple test case:
sh remove r*
Inside the remove script, I've whittled it down to just
echo $1
Here's the directory contents:
$ ls
file2 file4 newTestFile remove_engine restore
file3 fileName_1234 remove remove_w restore_engine
And here's what I get back.
$ sh remove r*
remove
I understand that BASH expands the wildcard out even before the script is executed. But why am I not getting all of the files in the directory that match f*?
Pathname expansion, aka globbing, expands a single shell word into multiple. In your case
./remove r*
is entirely identical to running
./remove remove remove_engine restore remove remove_w restore_engine
As you discovered, $1 will be remove because this is the first argument. The rest of the files are separate positional parameters, so $2 will be remove_engine and $3 will be restore.
To process all of the arguments, you use "$#", either in a loop:
for file in "$#"
do
printf 'One of the matches was: %s\n' "$file"
done
or just directly in commands that also accept multiple parameters:
# Delete all matches
echo rm "$#"
I want to store /c/users/me/dir name into a variable to pass it to cd system call.
Works when typing:
$ cd '/c/users/me/dir name'
or
$ cd /c/users/me/dir\ name
but does not works if I store it:
$ dirname="'/c/users/me/dir name'"
$ cd $dirname
$ bash: cd: /c/users/me/dir: No such file or directory
the same result to:
$ dirname=('/c/users/me/dir name')
or
$ dirname=(/c/users/me/dir\ name)
Which is the right way to store it?
Double-quote your path variable with spaces, to preserve it,
dirName="/c/users/me/dir name"
cd "$dirName"
Actually, dirname is a shell built-in, recommend using an alternate name to avoid confusion with the actual command.
From the man bash page,
Enclosing characters in double quotes (‘"’) preserves the literal value of all characters within the quotes, with the exception of ‘$’, ‘`’, ‘\’, and, when history expansion is enabled, ‘!’.
While using a bash variable you should double-quote it to preserve its state.
x='/home/ps/temp/bla bla'
cd $x ### <----used without double quotes.
sh: cd: /home/ps/temp/bla: No such file or directory
cd "$x" ### <---While using a bash variable you should double-quote it to presever its state.
pwd
/home/ps/temp/bla bla
I know about escaping, quoting and stuff, but still have a problem.
I you have a script containing "cd $1", and call it with an argument containing spaces, cd will always return an error message - it stops at the first space and can't find the directory. I tried protecting the arguments in every way :
ls -l
+-rwx... script
+drwx... dir with spaces/
cat script
+cd $1
script dir with spaces
+cd: dir: no such file or directory
script "dir with spaces"
+cd: dir: no such file or directory
script dir\ with\ spaces
+cd: dir\: no such file or directory
but none will work.
I feel like I'm missing the obvious, thanks for enlightening me.
You need to quote the expansion of "$1" to prevent it from being word split as well as quoting the string passed to the script to prevent it from being word-split.
So
$ cat script.sh
cd -- "$1"
$ ./script.sh "dir with spaces"
Edit: As gniourf_gniourf correctly pointed out using -- as the first argument to cd prevents problems should paths ever start with -.
Use double quotes on the variable
cd "$1"