Hidden features of Bash

二次信任 提交于 2019-11-27 08:54:20

问题


Shell scripts are often used as glue, for automation and simple one-off tasks. What are some of your favorite "hidden" features of the Bash shell/scripting language?

  • One feature per answer
  • Give an example and short description of the feature, not just a link to documentation
  • Label the feature using bold title as the first line

See also:

  • Hidden features of C
  • Hidden features of C#
  • Hidden features of C++
  • Hidden features of Delphi
  • Hidden features of Python
  • Hidden features of Java
  • Hidden features of JavaScript
  • Hidden features of Ruby
  • Hidden features of PHP
  • Hidden features of Perl
  • Hidden features of VB.Net

回答1:


insert preceding line's final parameter

alt-. the most useful key combination ever, try it and see, for some reason no one knows about this one.

press it again and again to select older last parameters.

great when you want to do something else to something you used just a moment ago.




回答2:


If you want to keep a process running after you log out:

disown -h <pid>

is a useful bash built-in. Unlike nohup, you can run disown on an already-running process.

First, stop your job with control-Z, get the pid from ps (or use echo $!), use bg to send it to the background, then use disown with the -h flag.

Don't forget to background your job or it will be killed when you logout.




回答3:


Almost everything listed under EXPANSION section in the manual

In particular, parameter expansion:

$ I=foobar
$ echo ${I/oo/aa} #replacement
faabar
$ echo ${I:1:2}   #substring
oo
$ echo ${I%bar}   #trailing substitution
foo
$ echo ${I#foo}   #leading substitution
bar



回答4:


My favorite:

sudo !!

Rerun the previous command with sudo.




回答5:


More magic key combinations:

  • Ctrl + r begins a “reverse incremental search” through your command history. As you continue to type, it retrieves the most recent command that contains all the text you enter.

  • Tab completes the word you've typed so far if it's unambiguous.

  • Tab Tab lists all completions for the word you've typed so far.

  • Alt + * inserts all possible completions, which is particularly helpful, say, if you've just entered a potentially destructive command with wildcards:

    rm -r source/d*.c Alt + *
    rm -r source/delete_me.c source/do_not_delete_me.c

  • Ctrl + Alt + e performs alias, history, and shell expansion on the current line. In other words, the current line is redisplayed as it will be processed by the shell:

    ls $HOME/tmp Ctrl Alt + e
    ls -N --color=tty -T 0 /home/cramey




回答6:


Get back history commands and arguments

It's possible to selectively access previous commands and arguments using the ! operator. It's very useful when you are working with long paths.

You can check your last commands with history.

You can use previous commands with !<n> being n the index of the command in history, negative numbers count backwards from the last command in history.

ls -l foo bar
touch foo bar
!-2

You can use previous arguments with !:<n>, zero is the command, >= 1 are the arguments.

ls -l foo
touch !:2
cp !:1 bar

And you can combine both with !<n>:<m>

touch foo bar
ls -l !:1 !:2
rm !-2:1 !-2:2
!-2

You can also use argument ranges !<n>:<x>-<y>

touch boo far
ls -l !:1-2

Other ! special modifiers are:

  • * for all the arguments

    ls -l foo bar
    ls !*
    
  • ^ for the first argument (!:1 == !^)

  • $ for the last argument

    ls -l foo bar
    cat !$ > /dev/null
    



回答7:


I like the -x feature, allowing to see what's going on in your script.

bash -x script.sh 



回答8:


SECONDS=0; sleep 5 ; echo "that took approximately $SECONDS seconds"

SECONDS

Each time this parameter is referenced, the number of seconds since shell invocation is returned. If a value is assigned to SECONDS, the value returned upon subsequent references is the number of seconds since the assignment plus the value assigned. If SECONDS is unset, it loses its special properties, even if it is subsequently reset.




回答9:


Here is one of my favorites. This sets tab completion to not be case sensitive. It's really great for quickly typing directory paths, especially on a Mac where the file system is not case sensitive by default. I put this in .inputrc in my home folder.

set completion-ignore-case on



回答10:


The special variable random:

if [[ $(($RANDOM % 6)) = 0 ]]
    then echo "BANG"
else
    echo "Try again"
fi   



回答11:


Regular expression handling

Recent bash releases feature regular expression matching, so you can do:

if [[ "mystring" =~ REGEX ]] ; then  
    echo match
fi

where REGEX is a raw regular expression in the format described by man re_format.

Matches from any bracketed parts are stored in the BASH_REMATCH array, starting at element 1 (element 0 is the matched string in its entirety), so you can use this to do regex-powered parsing too.




回答12:


Ctrlx Ctrle

This will load the current command into the editor defined in the variable VISUAL. This is really useful for long commands like some of those listed here.

To use vi as your editor:

export VISUAL=vi



回答13:


Quick & Dirty correction of typos (especially useful for long commands over slow connections where using the command history and scrolling through it would be horrible):

$ cat /proc/cupinfo
cat: /proc/cupinfo: No such file or directory
$ ^cup^cpu

Also try !:s/old/new which substitutes old with new in the previous command once.

If you want to substitute many occurrences you can do a global substitution with !:gs/old/new.

You can use the gs and s commands with any history event, e.g.

!-2:s/old/new

To substitute old with new (once) in the second to last command.




回答14:


Here two of my favorites:

To check the syntax w/o really executing the script use:

bash -n script.sh

Go back to the last directory (yes I know pushd and popd, but this is quicker)

cd -



回答15:


Using Infix Boolean Operators

Consider the simple if:

if [ 2 -lt 3 ]
    then echo "Numbers are still good!"
fi

That -lt looks kinda ugly. Not very modern. If you use double brackets around your boolean expression you can the normal boolean operators!

if [[ 2 < 3 ]]
    then echo "Numbers are still good!"
fi



回答16:


Arrays:

#!/bin/bash

array[0]="a string"
array[1]="a string with spaces and \"quotation\" marks in it"
array[2]="a string with spaces, \"quotation marks\" and (parenthesis) in it"

echo "There are ${#array[*]} elements in the array."
for n in "${array[@]}"; do
    echo "element = >>${n}<<"
done

More details on arrays (and other advanced bash scripting stuff) can be found in the Advanced Bash-Scripting Guide.




回答17:


Running a command before displaying the bash prompt

Set a command in the "PROMPT_COMMAND" env variable and it will be run automatically before each prompt. Example:

[lsc@home]$ export PROMPT_COMMAND="date"
Fri Jun  5 15:19:18 BST 2009
[lsc@home]$ ls
file_a  file_b  file_c
Fri Jun  5 15:19:19 BST 2009
[lsc@home]$ ls

For the next april fools, add "export PROMPT_COMMAND=cd" to someone's .bashrc then sit back and watch the confusion unfold.




回答18:


Magic key combinations from the bash man pages:

  • Ctrl + a and Ctrl + e move the cursor to the beginning and end of the current line, respectively.

  • Ctrl + t and Alt + t transpose the character and word before the cursor with the current one, then move the cursor forward.

  • Alt + u and Alt + l convert the current word (from the cursor to the end) to uppercase and lowercase.

    Hint: Press Alt + followed by either of these commands to convert the beginning of the current word.


Bonus man tips:

  • While viewing man pages, use / to search for text within the pages. Use n to jump ahead to the next match or N for the previous match.

  • Speed your search for a particular command or sub-section within the man pages by taking advantage of their formatting:

    o Instead of typing /history expansion to find that section, try /^history, using the caret (^) to find only lines that begin with "history."

    o Try /   read, with a few leading spaces, to search for that builtin command. Builtins are always indented in the man pages.




回答19:


export TMOUT=$((15*60))

Terminate bash after 15 minutes of idle time, set to 0 to disable. I usually put this to ~/.bashrc on my root accounts. It's handy when administrating your boxes and you may forget to logout before walking away from the terminal.




回答20:


Undo

C-S-- Control Shift Minus Undo-es typing actions.

Kill / Yank

Any delete operation C-w (delete previous word), C-k (delete to end of line), C-u (delete to start of line) etc... copies it's deleted text to the kill ring, you can paste the last kill with: C-y and cycle through (and paste from) the ring of deleted items with Alt-y




回答21:


You can ignore certain files while tab completing by setting th FIGNORE variable.

For example, if you have a subverion repo and you want to navigate more easily do

export FIGNORE=".svn"

now you can cd without being blocked by .svn directories.




回答22:


Using arithmetic:

if [[ $((2+1)) = $((1+2)) ]]
    then echo "still ok"
fi



回答23:


Brace expansion

Standard expansion with {x,y,z}:

$ echo foo{bar,baz,blam}
foobar foobaz fooblam
$ cp program.py{,.bak}  # very useful with cp and mv

Sequence expansion with {x..y}:

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
$ echo {a..f}{0..3}
a0 a1 a2 a3 b0 b1 b2 b3 c0 c1 c2 c3 d0 d1 d2 d3 e0 e1 e2 e3 f0 f1 f2 f3



回答24:


I recently read Csh Programming Considered Harmful which contained this astounding gem:

Consider the pipeline:

A | B | C

You want to know the status of C, well, that's easy: it's in $?, or $status in csh. But if you want it from A, you're out of luck -- if you're in the csh, that is. In the Bourne shell, you can get it, although doing so is a bit tricky. Here's something I had to do where I ran dd's stderr into a grep -v pipe to get rid of the records in/out noise, but had to return the dd's exit status, not the grep's:

device=/dev/rmt8
dd_noise='^[0-9]+\+[0-9]+ records (in|out)$'
exec 3>&1
status=`((dd if=$device ibs=64k 2>&1 1>&3 3>&- 4>&-; echo $? >&4) |
    egrep -v "$dd_noise" 1>&2 3>&- 4>&-) 4>&1`
exit $status;



回答25:


Truncate content of a file (zeroing file)

> file

Specifically, this is very good for truncating log files, when the file is open by another process, which still may write to the file.




回答26:


Not really a feature but rather a direction: I found many "hidden features", secrets and various bash usefulness at commandlinefu.com. Many of the highest rated answers to this answers, I learned them on that site :)




回答27:


Another small one: Alt+#

comments out the current line and moves it into the history buffer.

So when you're assembling a command line and you need to issue an interim command to e.g. find a file, you just hit alt+#, issue the other command, go up in the history, uncomment and proceed.




回答28:


Braces in lieu of do and done in for loop

For loop body are usually in do...done (just an example):

for f in *;
do
    ls "$f";
done

But we can use a C style using braces:

for f in *; {
    ls "$f";
}

I think this looks better than do...doneand I prefer this one. I have not yet found this in any Bash documentation, so this is really a hidden feature.




回答29:


C style numeric expressions:

let x="RANDOM%2**8"
echo -n "$x = 0b"
for ((i=8; i>=0; i--)); do
  let n="2**i"
  if (( (x&n) == n )); then echo -n "1"
  else echo -n "0"
  fi
done
echo ""



回答30:


These properties are another one of my favorites.

export HISTCONTROL=erasedups
export HISTSIZE=1000

The first one makes sure bash doesn't log commands more than once, will really improves history's usefulness. The other expands the history size to 1000 from the default of 100. I actually set this to 10000 on my machines.



来源:https://stackoverflow.com/questions/211378/hidden-features-of-bash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!