Bash technique: a simple and fast shell script for CD to multi-level parent directory

Time:2020-11-27

In Linux, when you need to CD to a multi-level parent directory, you need to entercd ../../../And so on.

When the input speed is relatively fast, there will be an extra dot or less input in the middle, which needs to be deleted and re entered.

In the actual development work, especially in the Android system development work, the source code directory may have multi-level subdirectories.

When you enter a subdirectory with a very deep directory structure, if you want to return to a specific multi-level parent directory, you need to manually enter multiple “…”, which is very troublesome and easy to enter incorrectly.

In order to simplify the input and reduce the input errors, this paper introduces a new method calledcdup.shShell script.

The script receives an integer parameter, which specifies how many layers of superior directory to return to. It can easily return to the multi-level superior directory and improve the work efficiency.

For example, executionsource cdup.sh 5Command, equivalent to executioncd ../../../../../Command.

The number 5 given specifies that you want to return to the level 5 parent directory.

You’ll see how to set command aliases to avoid typingsource cdup.shThese characters can be simplified intoup 5Such input is enough.

Because shell scripts run in the subshell by default, thecdThe command can only change the working directory of the current shell.

So it’s executed in a shell scriptcdThe command cannot change the working directory of the parent shell.

In order to make the script after execution, thecdAfter the directory, you need to usesourceCommand to execute the shell script.

usesourceCommand execution shell script, will run in the current shell, not in the sub shell.

Script code

listcdup.shThe specific code of the script is shown below.

In this code, for most of the key code are provided with detailed comments, easy to read.

Some key points will also be explained at the end of this article to help understand.

#!/bin/bash
#When you want to CD to a multi-level parent directory, you need to enter CD.. /..., and so on
#In order to simplify the input, the current script can handle an integer parameter that specifies how many levels of the parent directory to return to
#For example, source cdup.sh  3 is equivalent to CD.. //
#In order to keep the script in the directory behind the CD after the execution of the script, you need to use the source command
#You can add the following aliases to the ~ /. Bashrc file to facilitate execution:
#   alias up='source cdup.sh'
#The subsequent execution of the up 3 command is equivalent to CD.. //
#It is assumed here cdup.sh  The script is placed in the address directory specified by path. For example, the / usr / bin directory
#If cdup.sh  The script is not placed in the default addressing directory. Please specify the full absolute path

cdup_show_help()
{
printf "USAGE
    source cdup.sh number
OPTIONS
    Number: Specifies the parent directory to return to
    For example, source cdup.sh  3 is equivalent to CD.. //
NOTE
    You can use alias up = source cdup.sh 'set up alias to facilitate execution
"
}

if [ $# -ne 1 ]; then
    cdup_show_help
    #The current script is expected to be called by the source command and cannot execute the exit command,
    #Otherwise, the shell calling the script will exit. Return through the return command
    return 1
fi

UPDIR_PATH="../"

#According to the numerical parameters passed in, calculate how many levels of directory to return to and write the results to standard output
count_updirs()
{
    #The first parameter given specifies how many levels of parent directory to return to
    local count=$1

    local updirs=""
    while ((--count >= 0)); do
        #When no arithmetic extension is used, bash's + = operator will splice strings by default
        #The following statement will concatenate multiple "...", and get the effect similar to "..."
        updirs+=${UPDIR_PATH}
    done
    echo ${updirs}
}

target_dir="$(count_updirs $1)"
#To specify that the alias alias is not used, execute the original CD command
\cd "${target_dir}"

return

Code key point description

It is recommended to set the command alias to execute the current script

As explained earlier, you need to usesourceCommand to executecdup.shScript so that after executing the script, you can keep thecdAfter the directory.

That is, when executing, it needs to be written assource cdup.sh

This requires more characters to be input, and it is easy to forget to provide themsourceCommand.

To facilitate input, it is recommended to set command aliases in script comments to execute the current script.

For example, in the~/.bashrcAdd the following statement to the file to set the command alias:

alias up='source cdup.sh'

After adding this statement, in the current terminal, it needs to be executedsource ~/.bashrcCommand, this alias will take effect.

You can also re open the terminal. In the newly opened terminal, the alias will take effect by default.

After the alias is in effect, it can be usedupCommand to executecdup.shscript.

For example,up 3The command is equivalent tosource cdup.sh 3Command.

It is assumed herecdup.shThe script is placed in the address directory specified by the path global variable, and can be executed through the file name without specifying the file path.

If the script is not placed in the default addressing directory, the absolute path of the file needs to be provided.

Prefix the CD command with a backslash to specify that aliases are not used

In Bash, setting command alias is supported.

If a command alias is set tocdString, then executecdCommand, the command specified by the alias is executed.

That is, the original switch working directory will not be executedcdCommand.

To avoid this problem, you can use the\cdTo specify that aliases are not usedcdThe command itself.

staycdup.shIn the script\cdThis method ensures that the working directory can be switched.

Check the description of alias alias in man bash. It is not explicitly mentioned that adding backslash before the command can not use alias.

This is a special usage based on the function of backslash escape character and the processing logic of alias.

Yes\cdThis method of writing can not use the command alias. The specific explanation is as follows.

See bash against Bash\The following is a description of:

A non-quoted backslash (\) is the escape character.

It preserves the literal value of the next character that follows, with the exception of <newline>.

That is, when the backslash\When not enclosed in any quotation marks, it can keep the next character that follows it as its own.

Moreover, after bash treatment, it will be removed\Character, only the next character itself is retained.

For example, in Bash, without any quotation marks,\cYou get the character C.

Specific examples are as follows:

$ echo \c
c
$ echo \cd
cd

As you can see,echo \cThe command prints the character c, not the “C” string.

echo \cdThe command prints the “CD” string.

In fact, this is\cAfter getting the character c, the character c is combined with the character d to form a “CD” string.

Check the description of alias alias in man bash as follows:

The first word of each simple command, if unquoted, is checked to see if it has an alias.

If so, that word is replaced by the text of the alias.

That is, bash gets the first character of the command that is not enclosed in quotation marks and checks whether it is an alias based on this character.

If so, replace the command alias with the specified command.

In Bash, you cannot set the first character of a command alias to backslash.

Specific examples are as follows:

$ alias \testcd='cd ../../'
$ alias | grep cd
alias testcd='cd ../../'

Here we go firstalias \testcd='cd ../../'Command to set the alias.

From the input parameters, the alias set looks like\testcd

But withaliasThe command prints out all command aliases and uses thegrepFilter out inclusioncdYes.

As you can see, the alias actually set istestcd

The first character of the alias is not a backslash.

As mentioned earlier, bash removes backslashes that are not enclosed in quotation marks.

Check out the online help link for GNU bashhttps://www.gnu.org/software/…It is explained as follows:

3.1.1 Shell Operation

2.Breaks the input into words and operators, obeying the quoting rules described in Quoting. These tokens are separated by metacharacters. Alias expansion is performed by this step.

4.Performs the various shell expansions, breaking the expanded tokens into lists of filenames and commands and arguments.

3.5.9 Quote Removal

After the preceding expansions, all unquoted occurrences of the characters ‘’, ‘’’, and ‘”’ that did not result from one of the above expansions are removed.

That is, Bash is in theQuote RemovalThe phase removes the backslash\Character.

andQuote RemovalAfter the shell extension.

The alias is judged before the shell extension.

So bash also sees backslashes when judging aliases\Character.

In other words, execution\cdCommand, in the alias phase, you will see that the first character of this command is\

As explained earlier, you cannot set the first character of a command alias as a backslash.

Then we can’t find it\cdThe corresponding alias will not be replaced.

afterQuote RemovalAfter the phase, the backslash is removed and only thecdCommand.

At this point, the stage of judging the alias name has passed, so thecdThe command itself does not execute thecdCommand alias for.

That is, strictly speaking, putting a backslash before a command does not mean that you don’t use aliases.

Instead, aliases that start with a backslash are not found, so no alias extension is made.

Generally speaking, it can be simply understood as adding a backslash before the command to specify that no alias is used.

Example of executing the current script

Use theupCommand alias to executesource cdup.shThe specific execution results are as follows:

[frameworks/base/services/core/java/com/android/server]$ cd ../../../../../
[frameworks/base/services]$ cd -
frameworks/base/services/core/java/com/android/server
[frameworks/base/services/core/java/com/android/server]$ up 5
[frameworks/base/services]$

In this example, the directory path shown in the middle of square brackets is the working directory of the current shell.

As you can see,cd ../../../../../The command returns to the level 5 parent directory.

Then executecd -The command returns to the original subdirectory.

Re executionup 5Command, also back to the fifth level of the superior directory.

up 5The effect of the command followscd ../../../../../The command is the same, but the input is very simple and not easy to make mistakes.