bashoneliners.com
Welcome to bashoneliners.com, a place for practical or just pure awesome bash one-liners or shell script tips and tricks for GNU Linux, UNIX or BSD systems. You can post a bash one-liner to share it with the world, or browse or search the one-liners contributed by others.
You can follow the latest bash one-liners via rss feeds or on twitter @bashoneliners.
Tags
One-Liners
Faster imaging with dd
bash$ dd if=/dev/sda bs=$(hdparm -i /dev/sda | grep BuffSize | cut -d ' ' -f 3 | tr [:lower:] [:upper:] | tr -d BUFFSIZE=,) conv=noerror | dd of=image.dd conv=noerror
Explanation
GNU dd (disk dump) copies any block device to another block device or file.
- Piping the input of dd into the output of another instance seems to always improve copying speed.
- /dev/sda refers to your input device, which may vary. Check yours with fdisk -l.
- image.dd refers to the copy stored in the current working directory. You can also use another block device, such as /dev/sdb.
- The entire hdparm subshell sets dd's input block size to the buffer size of the source medium. This also usually improves copy speed, but may need adjustment (see limitations below).
- conv=noerror tells dd to ignore read errors.
Check dd's progress with: kill -USR1 $(pidof dd)
Limitations
The hdparm subshell is not appropriate for block devices without buffers, like flash drives. Try block sizes from 512 bytes to 1 or 2MiB to get the best speed.
Run a never-ending process in the background in a way that you can check on its status anytime
bash$ screen -d -m -S some_name ping my_router
Explanation
-d -mstartscreenin "detached" mode. This creates a new session but doesn't attach to it.-S some_namegives the screen session a label, so you can easily re-attach to it withscreen -R some_name- The shell prompt returns immediately, and of course you can logout too, the
screensession will continue to run happily. - When the specified program exits (in this example
ping), itsscreenwindow will also exit, and if there are no other windows in thescreensession, then thescreensession itself will also exit.
Make a hexdump or do the reverse with the xxd command
bash$ xxd /path/to/binary/file
Explanation
This shows a very nice hexdump of the specified file. You can edit the output and convert it back to binary with xxd -r. But the best part is that you can configure vim to do all the converting back and forth for you, effectively turning vim into a binary editor, by adding this to your .vimrc:
augroup Binary
au!
au BufReadPre *.bin let &bin=1
au BufReadPost *.bin if &bin | %!xxd
au BufReadPost *.bin set ft=xxd | endif
au BufWritePre *.bin if &bin | %!xxd -r
au BufWritePre *.bin endif
au BufWritePost *.bin if &bin | %!xxd
au BufWritePost *.bin set nomod | endif
augroup END
This will work for editing .bin files. To use it for other file extensions too, duplicate the lines within augroup and replace *.bin with *.exe for example.
This tip is from vim's :help xxd.
Really lazy way to print the first instance of $foo that occurs after $bar
bash$ ifconfig | grep ^en1 -A5 | grep inet | head -n 1
Explanation
This is just for the sake of an example of finding $foo that occurs after $bar. Substitute ifconfig and the arguments of grep appropriately for your use case.
- In the output of
ifconfigthere are several lines withinet. We want to get to the first one that comes after a line starting withen1 grep ^en1 -A5will print the line starting withen1and the next 5 lines that follow itgrep inetwill print only the lines matchinginethead -n 1will print only the first line
The value 5 in -A5 is really just a guess that the line we're interested in will be within the next 5 lines, the appropriate number depends on your use case.
Kind of a dumb technique, but it's easy to remember.
Print the first instance of $foo that occurs after $bar
bash$ sed -n '\@interface Ethernet3/1@,/!/ s/ip address/&/p' file...
Explanation
Should have realized this can be done with sed, too - it's even shorter!
-n suppresses the normal printing of lines.
\@interface Ethernet3/1@,/!/ specifies the beginning and end of the stanza we want to work on, and addresses all lines in between. Because the string we want to match at the beginning contains a slash, we need to designate a different character as the delimiter for the regular expression (@ in this case - note the backslash before the first one). We are OK using slashes for the ending regex, which matches an exclamation mark.
While in the stanza of interest, we slightly abuse the s function with s/ip address/&/p. This matches the text "ip address" and replaces it with... exactly the same thing (& just represents the matched text). The reason we bother doing this is to take advantage of the p flag, which will print any line where a replacement has been made.
The result is that only lines which match "ip address" will be printed, and only when we are in the stanza of interest.
Limitations
sed is a little less flexible - it uses basic regular expressions while awk uses extended regular expressions. In addition, awk has a fuller set of functions and operators available, which can be used to create a wider variety of tests and actions.
Print the first instance of $foo that occurs after $bar
bash$ awk '/interface Ethernet3\/1/ {instanza=1} /!/ {instanza=0} instanza && /ip address/ {print}' file...
Explanation
This can easily be done by setting a variable once the lead line of the stanza is found. Take the following file as input:
interface Ethernet3/1
ip address 172.30.10.2 255.255.0.0
ip router isis area1
no ip route-cache
isis metric 10
!
interface Ethernet3/2
ip address 192.168.10.10 255.255.255.0
ip router isis area1
no ip route-cache
isis metric 10
The first awk pattern-action pair in the one-liner /interface Ethernet3\/1/ {instanza=1} sets the instanza variable to 1 (true) when it encounters the "interface Ethernet 3/1" line. The second pattern-action /!/ {instanza=0} resets instanza back to 0 (false) once an exclamation point is encountered, ending the desired stanza. The third pattern-action instanza && /ip address/ {print} prints any line containing "ip address," but only if we are still in the desired stanza (instanza is true): in this case, ip address 172.30.10.2 255.255.0.0.
If you only wanted to see the first matching line of the first matching stanza, you could change the third action to be {print; exit} and awk would quit after printing the first match.
Sort du output in Human-readable format
bash$ du -hsx * | sort -rh
Explanation
sort supports -h for human readable number sorting.
Limitations
Probably a newer GNU only option for sort. :D
Replace symlinks with the actual files they are pointing at
bash$ find /path/to/dir -type l -exec sh -c 'cp --remove-destination "$(readlink "{}")" "{}"' \;
Explanation
- All the double quoting is necessary to handle filenames with spaces.
- Calling
shwith-execis necessary to evaluatereadlinkfor each symlink
Limitations
The BSD implementation of cp does not have the --remove-destination flag.
Expire a user's password immediately
bash$ chage -d 0 USERNAME
Explanation
This will effectively force the user to change his/her password at next login.
Limitations
Not in BSD. Yes in Linux. Don't know in UNIX.
Convert any 16:9 video to play on a QHD widescreen Android phone
bash$ ffmpeg -i $1 -y -threads 0 -subq 6 -deinterlace -level 30 -f mp4 -acodec libfaac -ab 160k -ar 24000 -ac 2 -vcodec libx264 -b 1000k -maxrate 1000k -bufsize 2000k -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -coder 0 -refs 2 -flags +loop -vol 256 -trellis 1 -me_method umh -async 1 $2
Explanation
The Android video player is somewhat fussy about the formats it can play. This ffmpeg script will take any movie file in 16:9 widescreen format and convert it into a form that can be played on one of the current leading QHD phones such as HTC Sensation, Samsung Galaxy S2 or Motorola Atrix. Files that are merely marked as widescreen (e.g. DVD VOBs) will have to be processed into a true widescreen format such as .m2t first.
Parameters:
$1 the input file
$2 the output file
Output file suffix should be .mp4
Requires:
ffmpeg and codecs
Create a visual report of the contents of a usb drive
bash$ find /path/to/drive -type f -exec file -b '{}' \; -printf '%s\n' | awk -F , 'NR%2 {i=$1} NR%2==0 {a[i]+=$1} END {for (i in a) printf("%12u %s\n",a[i],i)}' | sort -nr
Explanation
versorge asks:
I have a bunch of usb volumes lying around and I would like to get a quick summary of what is on the drives. How much space is taken up by pdf, image, text or executable files. This could be output as a text summary, or a pie chart.
This one-liner produces a list like this:
5804731229 FLAC audio bitstream data
687302212 MPEG sequence
99487460 data
60734903 PDF document
55905813 Zip archive data
38430192 ASCII text
32892213 gzip compressed data
24847604 PNG image data
16618355 XML 1.0 document text
13876248 JPEG image data
The find command locates all regular files (-type f) below the given directory, which could be a mounted USB stick or any other directory. For each one, it runs the file -b command with the filename to print the file type; if this succeeds, it also prints the file size (-printf '%s\n'). This results in a list containing a file type on one line, followed by the file size on the next.
The awk script takes this as input. The GNU file command often produces very specific descriptions such as GIF image data, version 87a, 640 x 480 - to generalize these, we set the field separator to be a comma with the -F option. Referencing $1 then only uses what's to the left of the first comma, giving us a more generic description like GIF image data.
In the awk script, the first pattern-action pair NR%2 {i=$1} applies to each odd-numbered line, setting the variable i to be the file type description. The even-numbered lines are handled by NR%2==0 {a[i]+=$1}, adding the value of the line (which is the file size) to the array variable a[i]. This results in an array indexed by file type, with each array member holding the cumulative sum of bytes for that type. The END { ... } pattern-action pair finally prints out a formatted list of the total size for each file type.
At the end of the line, the sort command sorts the list, putting the file types with the largest numbers at the top.
Limitations
This one-liner uses the -b option to file and the -printf primary of find - these are supported by the GNU utilities but may not work elsewhere. It can also take a long time to run, since it needs to open and analyze every file below the given directory.
Sort du output in Human-readable format
bash$ for i in G M K; do du -hsx * | grep "[0-9]$i\b" | sort -nr; done 2>/dev/null
Explanation
- The reason to use a
forloop is to sort results with G or M or K values separately, otherwisesort -nwould just sort everything by the numbers regardless of G M K suffix. grep "[0-9]$i\b"matches lines containing a digit followed by G or M or K followed by a "word boundary"
Sort du output in Human-readable format
bash$ for i in $(echo -e 'G\nM\nK'); do du -hsx /* 2>/dev/null | grep '[0-9]'$i | sort -rn; done
Explanation
- echo -e prints G for Gigabytes, M for Megabytes and K for Kilobytes in a line each.
- 2>/dev/null send stderr to /dev/null
- sort -rn sorts in reverse numerical order. Largest first
Convert a decimal number to octal, hexadecimal, binary, or anything
bash$ bc <<<'obase=2;1234'
Explanation
- <<< A variant of here documents
Convert a decimal number to octal, hexadecimal, binary, or anything
bash$ echo 'obase=2;1234' | bc
Explanation
bcis an arbitrary precision calculator language.obasedefines the conversion base for output numbers, in this example 2 (binary);is a statement separator inbc1234is the decimal number to convert- By piping the command to
bcwe get1234in binary format
Convert from avi format to mp4 encoding
bash$ ffmpeg -i file.avi file.mp4
Explanation
FFmpeg is a complete, cross-platform solution to record, convert and stream audio and video. It includes libavcodec - the leading audio/video codec library.
Limitations
It is not a standard package in most systems and distros.
Format input into multiple columns, like a table, useful or pretty-printing
bash$ mount | column -t
Explanation
column is a utility for formatting text. With the -t flag it detects the number of columns in the input so it can format the text into a table-like format.
For more details see man column.
A simple function to conveniently extract columns from an input stream
bash$ col() { awk '{print $'$(echo $* | sed -e 's/ /,$/g')'}'; }
Explanation
Something I do a lot is extract columns from some input where cut is not suitable because the columns are separated by not a single character but multiple spaces or tabs. So I often do things like:
... | awk '{print $7, $8}'
... which is a lot of typing, additionally slowed down when typing symbols like '{}$ ... Using the simple one-line function above makes it easier and faster:
... | col 7 8
How it works:
- The one-liner defines a new function with name
col - The function will execute
awk, and it expects standard input (coming from a pipe or input redirection) - The function arguments are processed with
sedto use them withawk: replace all spaces with,$so that for example1 2 3becomes1,$2,$3, which is inserted into theawkcommand to become the well formatted shell command:awk '{print $1,$2,$3}'
Resize an image proportionally to some specified width or height
bash$ mogrify -geometry x31 path/to/image.gif
Explanation
mogrifyis part ofImageMagick, an image manipulation software suitemogrifymanipulates the specified images. If you prefer to keep the original image untouched and write the manipulated image to a different file, simply replacemogrifywithconvert, the syntax is the same, but the last command line argument will be the target image to write to.- The
-geometryflag is to resize the image, it requires a dimension parameter in the formatWIDTHxHEIGHT - The dimension in this example has no width, which means the image will be resized to height=31 pixels, and the width will be proportional.
Limitations
ImageMagick is not a standard package, though it is open source and available in many systems.
Do something in another directory without going there
bash$ (cd /path/to/somewhere; tar c .) > somewhere.tar
Explanation
As explained superbly in man bash:
(list) list is executed in a subshell environment (see COMMAND EXECU-
TION ENVIRONMENT below). Variable assignments and builtin com-
mands that affect the shell's environment do not remain in
effect after the command completes. The return status is the
exit status of list.
In other words, this is a handy way to do something somewhere else without having to go there and coming back.
Remove carriage return '\r' character in many files, without looping and intermediary files
bash$ recode pc..l1 file1 file2 file3
Explanation
The recode utility installed on many systems converts between character sets. This command is shorthand for recode IBM-PC..latin1 file1 file2 file3 which converts the given files from CRLF to LF line endings.
Find the target path a symlink is pointing to
bash$ readlink a_symbolic_link_to_somewhere
Explanation
Sure, you could figure out the link target from the output of ls -l a_symbolic_link_to_somewhere too, but the output of readlink is simply the target of the symbolic link itself, so it is cleaner and easier to read.
Remove carriage return '\r' character in many files, without looping and intermediary files
bash$ vi +'bufdo set ff=unix' +'bufdo %s/^M$//' +q file1 file2 file3
Explanation
- The arguments starting with
+are commands invithat will be executed set ff=unixis a shortcut forset fileformat=unixand means to use "unix" file format, i.e. without the carriage return\rcharacter.%s/^M$//is a pattern substitution for all lines in the entire buffer, the pattern is "carriage return at end of the line", where^Mis not two literal characters but actually one, to enter it on the command line pressCtrlfollowed byEnter/Returnbufdomeans to run command in all buffers (each file is opened in a separate buffer)qis to quitvi
Note: the set ff=unix is necessary, otherwise the pattern substitution will not do anything if all the lines end with \r = the file is in dos format, because in that case the line ending character will not be considered as part of the line.
Note: if a shell-script has "accidentally" some carriage returns in it, then when you try to execute you may get an error: bad interpreter: No such file or directory. This one-liner fixes that problem. If you know that all the lines in the file have the carriage return, and there is only one file to fix, then a simplified version of the one-liner is enough:
vi +'set ff=unix' +wq file1
Get the octal, hexadecimal and decimal codes of the ASCII character set
bash$ man ascii
Explanation
Knowing the octal, hexadecimal or decimal code of the ASCII character set can be handy at times. In the past, too often I did things like:
perl -e 'for my $n (1 .. 255) { print $n, chr($n), $n, "\n"; }'
... when a simple man ascii would have done the trick...
On a related note, these all print the letter "A":
echo -e '\0101'
printf '\101'
printf '\x41'
perl -e 'print "\x41"'
Sort and remove duplicate lines in a file in one step without intermediary files
bash$ vi +'%!sort | uniq' +wq file.txt
Explanation
We open a file with vi and run two vi commands (specified with +):
%!sort | uniq%= range definition, it means all the lines in the current buffer.!= run filter for the range specified. Filter is an external program, in this examplesort | uniq
wq= write buffer contents to file and exit.