why this command does not work: cd `echo -n "~"` - bash

Running the command
cd \`echo -n "~"\`
I get the following error:
bash: cd: ~: No such file or directory
What's the problem if 'cd ~' works fine?

The issue is that bash does not do an additional expansion after command substitution. So while cd ~ is expanded the way you want, cd $(echo '~') does not.
There is a keyword called eval that was created for this sort of situation--it forces the command line to be expanded (evaluated) again. If you use eval on that line, it forces the ~ to be expanded into the user directory, even though the normal time for expansion has already passed. (Because the ~ does not exist until the echo command is run, and at that point, it's too late for expansion.)
eval cd `echo -n "~"`

If you do cd ~, the shell expands ~ to your home directory before executing the command. But if you use double quotes ("~"), then this is taken as a literal string and not expanded.
You can see the difference:
$ echo ~
/home/username
$ echo "~"
~
In order to have ~ expanded by the shell, you need to remove the double quotes.
The escaping behaviour of double quotes is described in the Bash manual: http://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html

You will also get the same issue if you simply do cd "~":
$ cd "~"
bash: cd: ~: No such file or directory
cd doesn't understand that ~ is special. It tries, and fails, to find a directory literally called ~.
The reason that cd ~ works is that bash edits the command before running it. bash replaces cd ~ with cd $HOME, and then expands $HOME to get cd /home/YourUsername.
Therefore,
cd `echo -n "~"`
becomes
cd "~"

Related

'sh' environment does not respect the PATH extensions, user's local PATH not in effect?

$ bl 1
$ sh -c 'bl 1'
sh: bl: command not found
The bl script is located in the user's PATH extension (/home/user/.local/bin) but the sh environment does not seem to be aware of it, the bash is. The main /usr/bin/sh executable symlinks to /usr/bin/bash.
Placing a symlink in /usr/local/bin pointing to the local bl script does seem to fix the issue. Expanding the PATH manually $ PATH=/usr/bin:$HOME/.local/bin sh -c 'bl 1' also solves it, something I don't really understand since both the bash and the sh are aware of the PATH.
$ export -p |grep PATH=
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/bin:~/.local/bin"
$ sh -c 'export -p |grep PATH'
export PATH="/usr/local/sbin:/usr/local/bin:/usr/bin:~/.local/bin"
"Something's missing and you have to find it" yet it's hard to look if you don't know what is missing.
$ export -p |grep PATH
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/bin:~/.local/bin"
Having a literal ~ is wrong. It should have been expanded to /home/user already. The shell will expand ~ when variables are assigned but not when they're expanded.
$ foo=~ && echo $foo # expanded at assignment
/home/user
$ foo='~' && echo $foo # not expanded since the assignment is quoted
~
Find the shell startup script where ~/.local/bin is added to the $PATH and make sure ~ is not quoted.
Wrong:
PATH="$PATH:~/.local/bin"
Right:
PATH=$PATH:~/.local/bin

bash pattern with asterisk

I have the following simple bash script:
#!/bin/bash -fx
ls *sh
The problem is that bash add a quote to the pattern and I get wrong output.
+ ls '*sh'
ls: cannot access *sh: No such file or directory
How can I change this behavior?
The output of ls *sh from the terminal is:
$ls *sh
a.bash a.sh b.sh
I tried to add quotes according to this post - "Bash variable containing file wildcard"
without success
That because you're disabling pathname expansion with the -f option.
#!/bin/bash -fx
From man:
-f
Disable filename expansion (globbing).

bash: evaluate substitution and replace on command line

Someone once showed me how to replace the current command (input line) with the output from a substitution.I suspect it is a readline function but can not remember which.The idea is basically that if you type in something like
$ cd `pwd` <READLINE-MACRO such as M-b or C-a>
then the command line will become:
$ cd /home/username/files
and after you run the command the history file will have cd /home/username/files as opposed to 'cd `pwd`'
According to ยง 8.4.8 "Some Miscellaneous [Readline] Commands" of the Bash Reference Manual:
shell-expand-line (M-C-e)
Expand the line as the shell does. This performs alias and history expansion as well as all of the shell word expansions (see Shell Expansions).
So, just type your command:
cd `pwd`
Then hit Alt+Ctrl+e to effect the command-substitution:
cd /home/username/files
Then hit Enter.
I guess you are finding such case: first you run
cd `pwd`
and then when you run:
!cd
it will seach your history cd command and replace `pwd` to /home/username/files
is it?

How to get name of alias that invoked bash script

$0 expands to the name of the shell script.
$ cat ./sample-script
#!/bin/bash
echo $0
$ chmod 700 ./sample-script
$ ./sample-script
./sample-script
If the shell script is invoked via a symbolic link, $0 expands to its name:
$ ln -s ./sample-script symlinked-script
$ ./symlinked-script
./symlinked-script
How could I get the name of an alias? Here `$0' expands again to the filename:
$ alias aliased-script=./sample-script
$ aliased-script
./sample-script
Aliases are pretty dumb, according to the man page
...Aliases are expanded when a command is read, not when it is executed...
so since bash is basically just replacing a string with another string and then executing it, there's no way for the command to know what was expanded in the alias.
I imagine you already know this, but for the record the answer is: you need cooperation by the code implementing the alias.
alternate_name () {
MY_ALIAS_WAS=alternate_name real_name "$#"
}
or, if you really want to use the superseded alias syntax:
alias alternate_name="MY_ALIAS_WAS=alternate_name real_name"
...and then...
$ cat ~/bin/real_name
#!/bin/sh
echo $0, I was $MY_ALIAS_WAS, "$#"
bash does not make this available. This is why symlinks are used to invoke multiplex commands, and not aliases.

Bash: in directory `directory_name.git`, how to cd to `../directory_name`?

I'm pretty new to Bash scripting and am looking to do the following:
The script's pwd is "/a/b/c/directory_name.git/" and I'd like to cd to "../directory_name" where directory_name could be anything. Is there any easy way to do this?
I'm guessing I'd have to put the result of pwd in a variable and erase the last 4 characters.
tmpd=${PWD##*/}
cd ../${tmpd%.*}
or perhaps more simply
cd ${PWD%.*}
Test
$ myPWD="/a/b/c/directory_name.git"
$ tmpd=${myPWD##*/}
$ echo "cd ../${tmpd%.*}"
cd ../directory_name
*Note: $PWD does not include a trailing slash so the ${param##word} expansion will work just fine.
Try:
cd `pwd | sed -e s/\.git$//`
The backticks execute the command inside, and use the output of that command as a command line argument to cd.
To debug pipelines like this, it's useful to use echo:
echo `pwd | sed -e s/\.git$//`
This should work:
cd "${PWD%.*}"
Didn't expect to get so many answers so fast so I had time to come up with my own inelegant solution:
#!/bin/bash
PWD=`pwd`
LEN=${#PWD}
END_POSITION=LEN-4
WORKING_COPY=${PWD:0:END_POSITION}
echo $WORKING_COPY
cd $WORKING_COPY
There's probably one above that's much more elegant :)
That's what basename is for:
cd ../$(basename "$(pwd)" .git)

Resources