bash use second argument of previous command - bash

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).

Related

what does 'cd $_' mean?

I have seen this command in a tutorial to create a new directory:
mkdir my-new-project && cd $_
I know mkdir my-new-project command is used to create a new directory, but what does cd $_ do?
$_ expands to the last argument to the previous simple command* or to previous command if it had no arguments.
mkdir my-new-project && cd $_
^ Here you have a command made of two simple commands. The last argument to the first one is my-new-project so $_ in the second simple command will expand to my-new-project.
To give another example:
echo a b; echo $_
#Will output:
#a b
#b
In any case, mkdir some_dir && cd $_ is a very common combo. If you get tired of typing it, I think it's a good idea to make it a function:
mkdircd() {
#Make path for each argument and cd into the last path
mkdir -p "$#" && cd "$_"
}
*
The bash manual defines a simple command as "a sequence of optional variable assignments followed by blank-separated words and redirections, and terminated by a control operator. " where control operator refers to one of || & && ; ;; ( ) | |& <newline>.
In practice$_ works like I've described but only with ||, &&, ;, or newline as the control operator.
7. How to create directory and switch to it using single command. As you might already know, the && operator is used for executing multiple commands, and $_ expands to the last argument of the previous command.
Quickly, if you want, you can create a directory and also move to that directory by using a single command. To do this, run the following command:
$ mkdir [dir-name] && cd $_
For those coming from Udacity's Version Control with Git, HowToForge offers a great explanation, here.

In shell script, how to change current directory safely with variable?

The following shell script changes current the directory to the desktop.
v=~/Desktop/
cd $v
pwd # desktop
The following script changes the current directory to home directory instead of generating error.
cd $undefined_variable
pwd # home directory
echo $? # 0
I'm afraid that the script will remove important files if I misspelled a variable for new current directory.
Generally, how do you safely change current directory with variable in shell script?
Use:
cd ${variable:?}
if $variable is not defined or empty then bash will throw an error and exit. It's like the set -u option but not global through the file.
You can set -u to make bash exit with an error each time you expand an undefined variable.
You could use the test -d condition (checks whether the specified variable is a directory), i.e.
if [[ -d $undefined_variable ]]
then
cd $undefined_variable
echo "This will not be printed if $undefined_variable is not defined"
fi
See also here for further test options...
The Bourne Shells have a construct to substitute a value for undefined variables, ${varname-subtitution}. You can use this to have a safe fallback directory in case the variable is undefined:
cd "${undefined-/tmp/backupdir}"
If there is a variable named undefined, its value is substituted, otherwise /tmp/backupdir is substituted.
Note that I also put the variable expansion in double quotes. This is used to prevent word splitting on strings containing spaces (very common for Windows directories). This way it works even for directories with spaces.
For the gory details on all the shell substitution constructs (there are seven more for POSIX shells), read your shell manual's Parameter Substitution section.
You have to write a wrapper (this work in bash):
cd() {
if [ $# -ne 1 ] ;then
echo "cd need exactly 1 argument" >&2
return 2
fi
builtin cd "$1"
}
yes, that's shell
if you type cd without parameter it will jump to home dir.
You can can check the variable of null or empty before you cd command.
check like (cd only be called if targetDir is not empty):
test -z "$targetDir" || cd $targetDir
check like (cd only be called if targetDir really exist):
test -d "$targetDir" && cd $targetDir
Note: Thanks for -1, should read the last sentence too. So I added the real answer.

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?

bash script to change directory and execute command with arguments

I am trying to do the following task:
write a shell script called changedir which
takes a directory name, a command name and (optionally) some additional arguments.
The script will then change into the directory indicated, and
executes the command indicated with the arguments provided.
Here an example:
$ sh changedir /etc ls -al
This should change into the /etc directory and run the command ls -al.
So far I have:
#!/bin/sh
directory=$1; shift
command=$1; shift
args=$1; shift
cd $directory
$command
If I run the above like sh changedir /etc ls it changes and lists the directory. But if I add arguments to the ls it does not work. What do I need to do to correct it?
You seemed to be ignoring the remainder of the arguments to your command.
If I understand correctly you need to do something like this:
#!/bin/sh
cd "$1" # change to directory specified by arg 1
shift # drop arg 1
cmd="$1" # grab command from next argument
shift # drop next argument
"$cmd" "$#" # expand remaining arguments, retaining original word separations
A simpler and safer variant would be:
#!/bin/sh
cd "$1" && shift && "$#"
Since there can probably be more than a single argument to a command, i would recommend using quotation marks. Something like this:
sh changedir.sh /etc "ls -lsah"
Your code would be much more readable if you ommited the 'shift':
directory=$1;
command=$2;
cd $directory
$command
or simply
cd DIRECTORY_HERE; COMMAND_WITH_ARGS_HERE

Is there a Bash shortcut for traversing similar directory structures?

The KornShell (ksh) used to have a very useful option to cd for traversing similar directory structures; e.g., given the following directories:
/home/sweet/dev/projects/trunk/projecta/app/models
/home/andy/dev/projects/trunk/projecta/app/models
Then if you were in the /home/sweet... directory then you could change to the equivalent directory in andy's structure by typing
cd sweet andy
So if ksh saw 2 arguments then it would scan the current directory path for the first value, replace it with the second and cd there. Is anyone aware of similar functionality built into Bash? Or if not, a hack to make Bash work in the same way?
Other solutions offered so far suffer from one or more of the following problems:
Archaic forms of tests - as pointed out by Michał Górny
Incomplete protection from directory names containing white space
Failure to handle directory structures which have the same name used more than once or with substrings that match: /canis/lupus/lupus/ or /nicknames/Robert/Rob/
This version handles all the issues listed above.
cd ()
{
local pwd="${PWD}/"; # we need a slash at the end so we can check for it, too
if [[ "$1" == "-e" ]]
then
shift
# start from the end
[[ "$2" ]] && builtin cd "${pwd%/$1/*}/${2:-$1}/${pwd##*/$1/}" || builtin cd "$#"
else
# start from the beginning
[[ "$2" ]] && builtin cd "${pwd/\/$1\///$2/}" || builtin cd "$#"
fi
}
Issuing any of the other versions, which I'll call cdX, from a directory such as this one:
/canis/lupus/lupus/specimen $ cdX lupus familiaris
bash: cd: /canis/familiaris/lupus/specimen: No such file or directory
fails if the second instance of "lupus" is the one intended. In order to accommodate this, you can use the "-e" option to start from the end of the directory structure.
/canis/lupus/lupus/specimen $ cd -e lupus familiaris
/canis/lupus/familiaris/specimen $
Or issuing one of them from this one:
/nicknames/Robert/Rob $ cdX Rob Bob
bash: cd: /nicknames/Bobert/Rob: No such file or directory
would substitute part of a string unintentionally. My function handles this by including the slashes in the match.
/nicknames/Robert/Rob $ cd Rob Bob
/nicknames/Robert/Bob $
You can also designate a directory unambiguously like this:
/fish/fish/fins $ cd fish/fins robot/fins
/fish/robot/fins $
By the way, I used the control operators && and || in my function instead of if...then...else...fi just for the sake of variety.
cd "${PWD/sweet/andy}"
No, but...
Michał Górny's substitution expression works nicely. To redefine the built-in cd command, do this:
cd () {
if [ "x$2" != x ]; then
builtin cd ${PWD/$1/$2}
else
builtin cd "$#"
fi
}

Resources