bash: evaluate substitution and replace on command line - bash

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?

Related

csh doesn't recognize command with command line options beginning with --

I have an rsync command in my csh script like this:
#! /bin/csh -f
set source_dir = "blahDir/blahBlahDir"
set dest_dir = "foo/anotherFoo"
rsync -av --exclude=*.csv ${source_dir} ${dest_dir}
When I run this I get the following error:
rsync: No match.
If I remove the --exclude option it works. I wrote the equivalent script in bash and that works as expected
#/bin/bash -f
source_dir="blahDir/blahBlahDir"
dest_dir="foo/anotherFoo"
rsync -av --exclude=*.csv ${source_dir} ${dest_dir}
The problem is that this has to be done in csh only. Any ideas on how I can get his to work?
It's because csh is trying to expand --exclude=*.csv into a filename, and complaining because it cannot find a file matching that pattern.
You can get around this by enclosing the option in quotes:
rsynv -rv '--exclude=*.csv' ...
or escaping the asterisk:
rsynv -rv --exclude=\*.csv ...
This is a consequence of the way csh and bash differ in their default treatment of arguments with wildcards that don't match a file. csh will complain while bash will simply leave it alone.
You may think bash has chosen the better way but that's not necessarily so, as shown in the following transcript where you have a file matching the argument:
pax> touch -- '--file=xyzzy.csv' ; ls -- *.csv
--file=xyzzy.csv
pax> echo --file=*.csv
--file=xyzzy.csv
You can see there that the bash shell expands the argument rather than giving it to the program as is. Both sides have their pros and cons.

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

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 "~"

Execute command with backquote in bash shell script

I write up a little shell script in bash that allows me to execute commands in sub-directories. Here is the script
bat.sh:
#!/bin/sh
for d in */; do
echo "Executing \"$#\" in $d"
cd $d
`$#`
cd ..
done
With my following directory structures
/home/user
--a/
----x.txt
----y.txt
--b/
----u.txt
----v.txt
I expect the following command to list out the content of directories a and b when it is executed in the home directory
bat.sh ls
The result is
Executing "ls" in a/
/home/user/bin/bat.sh: line 6: x.txt: command not found
Executing "ls" in b/
/home/user/bin/bat.sh: line 6: u.txt: command not found
Any idea on what is going wrong here?
You don't want the back quotes; you want double quotes.
#!/bin/sh
for d in */
do
echo "Executing \"$*\" in $d"
(cd "$d" && "$#")
done
You are trying to execute the output of the command you pass, whereas you simply want to execute the command.
The use of an explicit subshell (the ( … ) notation) may avoid some problems with symlinks that jump to other directories. It is, in my (perhaps archaic) view, a safer way to switch directories for the purposes of executing commands.

bash use second argument of previous command

how I can use the second argument of previous command in a new command ?
example, with
$ mkdir test
I make a directory, how I can use the name of directory for change to this ?
$ mkdir test && cd use_var
$_ is the last (right-most) argument of the previous command.
mkdir gash && cd "$_"
(I don't create files or directories called test, that's the name of a shell built-in and can cause confusions)
With history expansion, you can refer to arbitrary words in the current command line
mkdir dir1 && cd "!#:1"
# 0 1 2 3 4
!# refers to the line typed so far, and :1 refers to word number one (with mkdir starting at 0).
If you use this in a script (i.e., a non-interactive shell), you need to turn history expansion on with set -H and set -o history.
Pressing Esc + . places the last argument of previous command on the current place of cursor. Tested in bash shell and ksh shell.
I use functions for this. Type this in your shell:
mkcd() { mkdir "$1" ; cd "$1" ; }
Now you have a new command mkcd.
If you need this repeatedly, put the line into the file ~/.bash_aliases (if you use bash; other shells use different names).

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.

Resources