mirror of
https://github.com/flokoe/bash-hackers-wiki.git
synced 2024-11-01 06:53:05 +01:00
Convert snipplets to Markdown
This commit is contained in:
parent
82096dc0ff
commit
22386b0460
103
docs/snipplets/add_color_to_your_scripts.md
Normal file
103
docs/snipplets/add_color_to_your_scripts.md
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
# Add Color to your scripts
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags : terminal, color
|
||||||
|
LastUpdate_dt : 2013-03-23 Contributors : Frank Lazzarini, Dan Douglas
|
||||||
|
type : snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Make your scripts output more readable using bash colors. Simply add
|
||||||
|
these variables to your script, and you will be able to echo in color.
|
||||||
|
(I haven\'t added all the colors available, just some basics)
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
ESC_SEQ="\x1b["
|
||||||
|
COL_RESET=$ESC_SEQ"39;49;00m"
|
||||||
|
COL_RED=$ESC_SEQ"31;01m"
|
||||||
|
COL_GREEN=$ESC_SEQ"32;01m"
|
||||||
|
COL_YELLOW=$ESC_SEQ"33;01m"
|
||||||
|
COL_BLUE=$ESC_SEQ"34;01m"
|
||||||
|
COL_MAGENTA=$ESC_SEQ"35;01m"
|
||||||
|
COL_CYAN=$ESC_SEQ"36;01m"
|
||||||
|
|
||||||
|
Now if you want to output some text in color use *echo -e* instead of
|
||||||
|
just echo. And always remember to use the *\$COL_RESET* variable to
|
||||||
|
reset the color changes in bash. Like so \....
|
||||||
|
|
||||||
|
echo -e "$COL_RED This is red $COL_RESET"
|
||||||
|
echo -e "$COL_BLUE This is blue $COL_RESET"
|
||||||
|
echo -e "$COL_YELLOW This is yellow $COL_RESET"
|
||||||
|
|
||||||
|
But also see the notes in [the article about using
|
||||||
|
terminalcodes](/scripting/terminalcodes) about generating codes and
|
||||||
|
hardwiring codes.
|
||||||
|
|
||||||
|
This snipplet sets up associative arrays for basic color codes using
|
||||||
|
`tput` for Bash, ksh93 or zsh. You can pass it variable names to
|
||||||
|
correspond with a collection of codes. There\'s a `main` function with
|
||||||
|
example usage.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
${ZSH_VERSION+false} || emulate ksh
|
||||||
|
${BASH_VERSION+shopt -s lastpipe extglob}
|
||||||
|
|
||||||
|
# colorSet [ --setaf | --setab | --misc ] var
|
||||||
|
# Assigns the selected set of escape mappings to the given associative array names.
|
||||||
|
function colorSet {
|
||||||
|
typeset -a clrs msc
|
||||||
|
typeset x
|
||||||
|
clrs=(black red green orange blue magenta cyan grey darkgrey ltred ltgreen yellow ltblue ltmagenta ltcyan white)
|
||||||
|
msc=(sgr0 bold dim smul blink rev invis)
|
||||||
|
|
||||||
|
while ! ${2:+false}; do
|
||||||
|
${KSH_VERSION:+eval typeset -n "$2"=\$2}
|
||||||
|
case ${1#--} in
|
||||||
|
setaf|setab)
|
||||||
|
for x in "${!clrs[@]}"; do
|
||||||
|
eval "$2"'[${clrs[x]}]=$(tput "${1#--}" "$x")'
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
misc)
|
||||||
|
for x in "${msc[@]}"; do
|
||||||
|
eval "$2"'[$x]=$(tput "$x")'
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
esac
|
||||||
|
shift 2
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Example code
|
||||||
|
function main {
|
||||||
|
typeset -A fgColors bgColors miscEscapes
|
||||||
|
if colorSet --setaf fgColors --setab bgColors --misc miscEscapes; then
|
||||||
|
if ! ${1:+${fgColors[$1]:+false}}; then
|
||||||
|
printf '%s%s%s\n' "${fgColors[$1]}" "this text is ${1}" "${miscEscapes[sgr0]}" >&3
|
||||||
|
else
|
||||||
|
printf '%s, %s\n' "${1:-Empty}" 'no such color.'
|
||||||
|
typeset x y
|
||||||
|
for x in fgColors bgColors miscEscapes; do
|
||||||
|
typeset -a keys
|
||||||
|
eval 'keys=("${!'"$x"'[@]}")'
|
||||||
|
printf '%s=( ' "$x"
|
||||||
|
for y in "${keys[@]}"; do
|
||||||
|
eval 'printf "[%q]=%q " "$y" "${'"$x"'[$y]}"'
|
||||||
|
done
|
||||||
|
printf ')\n'
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo 'Failed setting color arrays.'
|
||||||
|
return 1
|
||||||
|
fi 3>&1 >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
|
|
||||||
|
# vim: set fenc=utf-8 ff=unix ft=sh :
|
||||||
|
```
|
45
docs/snipplets/awkcsv.md
Normal file
45
docs/snipplets/awkcsv.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Using `awk` to deal with CSV that uses quoted/unquoted delimiters
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags : awk, csv
|
||||||
|
LastUpdate_dt : 2010-07-31 Contributors : SiegX (IRC) type : snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
CSV files are a mess, yes.
|
||||||
|
|
||||||
|
Assume you have CSV files that use the comma as delimiter and quoted
|
||||||
|
data fields that can contain the delimiter.
|
||||||
|
|
||||||
|
"first", "second", "last"
|
||||||
|
"fir,st", "second", "last"
|
||||||
|
"firtst one", "sec,ond field", "final,ly"
|
||||||
|
|
||||||
|
Simply using the comma as separator for `awk` won\'t work here, of
|
||||||
|
course.
|
||||||
|
|
||||||
|
Solution: Use the field separator `", "|^"|"$` for `awk`.
|
||||||
|
|
||||||
|
This is an OR-ed list of 3 possible separators:
|
||||||
|
|
||||||
|
-------- -----------------------------------------------
|
||||||
|
`", "` matches the area between the datafields
|
||||||
|
`^"` matches the area left of the first datafield
|
||||||
|
`"$` matches the area right of the last data field
|
||||||
|
-------- -----------------------------------------------
|
||||||
|
|
||||||
|
You can tune these delimiters if you have other needs (for example if
|
||||||
|
you don\'t have a space after the commas).
|
||||||
|
|
||||||
|
Test:
|
||||||
|
|
||||||
|
The `awk` command used for the CSV above just prints the fileds
|
||||||
|
separated by `###` to see what\'s going on:
|
||||||
|
|
||||||
|
$ awk -v FS='", "|^"|"$' '{print $2"###"$3"###"$4}' data.csv
|
||||||
|
first###second###last
|
||||||
|
fir,st###second###last
|
||||||
|
firtst one###sec,ond field###final,ly
|
||||||
|
|
||||||
|
**ATTENTION** If the CSV data changes its format every now and then (for
|
||||||
|
example it only quotes the data fields if needed, not always), then this
|
||||||
|
way will not work.
|
15
docs/snipplets/filesize.md
Normal file
15
docs/snipplets/filesize.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Show size of a file
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: files, file size
|
||||||
|
LastUpdate_dt: 2010-07-31 Contributors: Frank Lazzarini type: snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
This is a simple snippet to echo the size of a file in bytes.
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
FILENAME=/home/heiko/dummy/packages.txt
|
||||||
|
FILESIZE=$(wc -c < "$FILENAME")
|
||||||
|
# non standard way (GNU stat): FILESIZE=$(stat -c%s "$FILENAME")
|
||||||
|
|
||||||
|
echo "Size of $FILENAME = $FILESIZE bytes."
|
71
docs/snipplets/kill_bg_job_without_message.md
Normal file
71
docs/snipplets/kill_bg_job_without_message.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# Kill a background job without a message
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: kill, process
|
||||||
|
management, jobs LastUpdate_dt: 2010-07-31 Contributors: Jan Schampera
|
||||||
|
type: snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
When you start background jobs from within a script (non-interactive
|
||||||
|
shell) and kill it afterwards, you will get a message from the shell
|
||||||
|
that the process was terminated.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# example background process
|
||||||
|
sleep 300 &
|
||||||
|
|
||||||
|
# get the PID
|
||||||
|
BG_PID=$!
|
||||||
|
|
||||||
|
# kill it, hard and mercyless
|
||||||
|
kill -9 $BG_PID
|
||||||
|
|
||||||
|
echo "Yes, we killed it"
|
||||||
|
|
||||||
|
You will get something like this:
|
||||||
|
|
||||||
|
$ ./bg_kill1.sh
|
||||||
|
./bg_kill1.sh: line 11: 3413 Killed sleep 300
|
||||||
|
Yes, we killed it
|
||||||
|
|
||||||
|
This is more or less a normal message. And it can\'t be easily
|
||||||
|
redirected since it\'s the shell itself that yells this message, not the
|
||||||
|
command `kill` or something else. You would have to redirect the whole
|
||||||
|
script\'s output.
|
||||||
|
|
||||||
|
It\'s also useless to temporarily redirect `stderr` when you call the
|
||||||
|
`kill` command, since the successful termination of the job, the
|
||||||
|
termination of the `kill` command and the message from the shell may not
|
||||||
|
happen at the same time. And a blind `sleep` after the `kill` would be
|
||||||
|
just a workaround.
|
||||||
|
|
||||||
|
The solution is relatively easy: The shell spits that message because it
|
||||||
|
controls the background job, and when it terminates, the shell will tell
|
||||||
|
you whenever possible. Now you just need to tell your shell that it is
|
||||||
|
no longer responsible for that background process. This is done by the
|
||||||
|
`disown` command, which can take an internal shell job number (like
|
||||||
|
`%1`) or a process ID as argument.
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# example background process
|
||||||
|
sleep 300 &
|
||||||
|
|
||||||
|
# get the PID
|
||||||
|
BG_PID=$!
|
||||||
|
|
||||||
|
### HERE, YOU TELL THE SHELL TO NOT CARE ANY MORE ###
|
||||||
|
disown $BG_PID
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
# kill it, hard and mercyless, now without a trace
|
||||||
|
kill -9 $BG_PID
|
||||||
|
|
||||||
|
echo "Yes, we killed it"
|
||||||
|
|
||||||
|
That way, you can run and kill background processes without disturbing
|
||||||
|
messages.
|
64
docs/snipplets/largestfile.md
Normal file
64
docs/snipplets/largestfile.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# Get largest file
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: directory, recursive,
|
||||||
|
find, crawl LastUpdate_dt: 2013-03-23 Contributors: Dan Douglas type:
|
||||||
|
snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
One basic pattern for recursive directory traversal with operations on
|
||||||
|
files at each node. This gets the largest file in each subdirectory.
|
||||||
|
Toggling some small details will make it return the smallest, or
|
||||||
|
traverse breadth-first instead of depth-first.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# GNU find + bash4 / ksh93v / zsh
|
||||||
|
# Get the largest file matching pattern in the given directories recursively
|
||||||
|
${ZSH_VERSION+false} || emulate ksh
|
||||||
|
${BASH_VERSION+shopt -s lastpipe extglob}
|
||||||
|
|
||||||
|
function getLargest {
|
||||||
|
typeset -A cur top || return
|
||||||
|
typeset dir x
|
||||||
|
for dir in "$2"/*/; do
|
||||||
|
[[ -d $dir ]] || return 0
|
||||||
|
getLargest "$1" "${dir%/}" || return
|
||||||
|
top[size]=-1
|
||||||
|
find "$dir" -maxdepth 1 -type f -name "$1" -printf '%s\0%f\0' | {
|
||||||
|
while :; do
|
||||||
|
for x in cur\[{size,name}\]; do
|
||||||
|
IFS= read -rd '' "$x" || break 2
|
||||||
|
done
|
||||||
|
if (( cur[size] > top[size] )); then
|
||||||
|
top[size]=${cur[size]} top[name]=${cur[name]}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
printf '%q\n' "${dir}${top[name]}"
|
||||||
|
}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# main pattern dir [ dir ... ]
|
||||||
|
function main {
|
||||||
|
if [[ -n $1 ]]; then
|
||||||
|
typeset dir pattern=$1
|
||||||
|
shift
|
||||||
|
for dir; do
|
||||||
|
[[ -d $dir ]] || return
|
||||||
|
getLargest "$pattern" "$dir"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
|
|
||||||
|
# vim: set fenc=utf-8 ff=unix ft=sh :
|
||||||
|
```
|
||||||
|
|
||||||
|
## More examples
|
||||||
|
|
||||||
|
- <http://mywiki.wooledge.org/BashFAQ/003>
|
||||||
|
- <http://mywiki.wooledge.org/UsingFind>
|
15
docs/snipplets/pause_command.md
Normal file
15
docs/snipplets/pause_command.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Pausing a script (like MSDOS pause command)
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: terminal, pause, input
|
||||||
|
LastUpdate_dt: 2010-07-31 Contributors: Jan Schampera type: snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
From the [example section of the read
|
||||||
|
command](/commands/builtin/read#examples), something that acts similar
|
||||||
|
to the MSDOS `pause` command:
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
local dummy
|
||||||
|
read -s -r -p "Press any key to continue..." -n 1 dummy
|
||||||
|
}
|
52
docs/snipplets/prargs.md
Normal file
52
docs/snipplets/prargs.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Print argument list for testing
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: debug, arguments
|
||||||
|
LastUpdate_dt: 2013-03-23 Contributors: Snappy (IRC), Dan Douglas type:
|
||||||
|
snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Sometimes you might find it useful to see how arguments passed to a
|
||||||
|
program arrive there.
|
||||||
|
|
||||||
|
Check this script (save it as script file or make a function):
|
||||||
|
|
||||||
|
printf '"%b"\n' "$0" "$@" | nl -v0 -s": "
|
||||||
|
|
||||||
|
It uses the [printf command](/commands/builtin/printf) to generate a
|
||||||
|
list of arguments, even with escape sequences interpreted. This list is
|
||||||
|
shown formatted by the nl(1) utility.
|
||||||
|
|
||||||
|
Another alternative with colorized output. If run in Bash, it
|
||||||
|
temporarily disables all debug output for itself, including the test
|
||||||
|
that determines whether to hide debug output. In ksh, tracing would have
|
||||||
|
to be enabled on the function to show debug output, so it works out to
|
||||||
|
being equivalent.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Bash or ksh93 debugging function for colored display of argv.
|
||||||
|
# Optionally set OFD to the desired output file descriptor.
|
||||||
|
function args {
|
||||||
|
{ BASH_XTRACEFD=3 command eval ${BASH_VERSION+"$(</dev/fd/0)"}; } <<-'EOF' 3>/dev/null
|
||||||
|
case $- in *x*)
|
||||||
|
set +x
|
||||||
|
trap 'trap RETURN; set -x' RETURN
|
||||||
|
esac
|
||||||
|
EOF
|
||||||
|
|
||||||
|
[[ ${OFD-1} == +([0-9]) ]] || return
|
||||||
|
|
||||||
|
if [[ -t ${OFD:-2} ]]; then
|
||||||
|
typeset -A clr=([green]=$(tput setaf 2) [sgr0]=$(tput sgr0))
|
||||||
|
else
|
||||||
|
typeset clr
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ${1+false}; then
|
||||||
|
printf -- "${clr[green]}<${clr[sgr0]}%s${clr[green]}>${clr[sgr0]} " "$@"
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
echo 'no args.'
|
||||||
|
fi >&"${OFD:-2}"
|
||||||
|
}
|
||||||
|
```
|
135
docs/snipplets/print_horizontal_line.md
Normal file
135
docs/snipplets/print_horizontal_line.md
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
# Print a horizontal line
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: terminal, line
|
||||||
|
LastUpdate_dt: 2010-07-31 Contributors: Jan Schampera, prince_jammys,
|
||||||
|
ccsalvesen, others type: snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The purpose of this small code collection is to show some code that
|
||||||
|
draws a horizontal line using as less external tools as possible (it\'s
|
||||||
|
not a big deal to do it with AWK or Perl, but with pure or nearly-pure
|
||||||
|
Bash it gets more interesting).
|
||||||
|
|
||||||
|
In general, you should be able to use this code to repeat any character
|
||||||
|
or character sequence.
|
||||||
|
|
||||||
|
## The simple way: Just print it
|
||||||
|
|
||||||
|
Not a miracle, just to be complete here.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
printf '%s\n' --------------------
|
||||||
|
```
|
||||||
|
|
||||||
|
## The iterative way
|
||||||
|
|
||||||
|
This one simply loops 20 times, always draws a dash, finally a newline
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
for ((x = 0; x < 20; x++)); do
|
||||||
|
printf %s -
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
```
|
||||||
|
|
||||||
|
## The simple printf way
|
||||||
|
|
||||||
|
This one uses the `printf` command to print an **empty** field with a
|
||||||
|
**minimum field width** of 20 characters. The text is padded with
|
||||||
|
spaces, since there is no text, you get 20 spaces. The spaces are then
|
||||||
|
converted to `-` by the `tr` command.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
printf '%20s\n' | tr ' ' -
|
||||||
|
```
|
||||||
|
|
||||||
|
whitout an external command, using the (non-POSIX) substitution
|
||||||
|
expansion and `-v` option:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
printf -v res %20s
|
||||||
|
printf '%s\n' "${res// /-}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## A line across the entire width of the terminal
|
||||||
|
|
||||||
|
This is a variant of the above that uses `tput cols` to find the width
|
||||||
|
of the terminal and set that number as the minimum field witdh.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
|
||||||
|
```
|
||||||
|
|
||||||
|
## The more advanced printf way
|
||||||
|
|
||||||
|
This one is a bit tricky. The format for the `printf` command is `%.0s`,
|
||||||
|
which specified a field with the **maximum** length of **zero**. After
|
||||||
|
this field, `printf` is told to print a dash. You might remember that
|
||||||
|
it\'s the nature of `printf` to repeat, if the number of conversion
|
||||||
|
specifications is less than the number of given arguments. With brace
|
||||||
|
expansion `{1..20}`, 20 arguments are given (you could easily write
|
||||||
|
`1 2 3 4 ... 20`, of course!). Following happens: The **zero-length
|
||||||
|
field** plus the dash is repeated 20 times. A zero length field is,
|
||||||
|
naturally, invisible. What you see is the dash, repeated 20 times.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Note: you might see that as ''%.s'', which is a (less documented) shorthand for ''%.0s''
|
||||||
|
printf '%.0s-' {1..20}; echo
|
||||||
|
```
|
||||||
|
|
||||||
|
If the 20 is variable, you can use [eval](/commands/builtin/eval) to
|
||||||
|
insert the expansion (take care that using `eval` is potentially
|
||||||
|
dangerous if you evaluate external data):
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
eval printf %.0s- '{1..'"${COLUMNS:-$(tput cols)}"\}; echo
|
||||||
|
```
|
||||||
|
|
||||||
|
Or restrict the length to 1 and prefix the arguments with the desired
|
||||||
|
character.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
eval printf %.1s '-{1..'"${COLUMNS:-$(tput cols)}"\}; echo
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also do it the crazy ormaaj way™ following basically the same
|
||||||
|
principle as this [string reverse
|
||||||
|
example](/commands/builtin/eval#expansion_side-effects). It completely
|
||||||
|
depends on Bash due to its brace expansion evaluation order and array
|
||||||
|
parameter parsing details. As above, the eval only inserts the COLUMNS
|
||||||
|
expansion into the expression and isn\'t involved in the rest, other
|
||||||
|
than to put the `_` value into the environment of the `_[0]` expansion.
|
||||||
|
This works well since we\'re not creating one set of arguments and then
|
||||||
|
editing or deleting them to create another as in the previous examples.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
_=- command eval printf %s '"${_[0]"{0..'"${COLUMNS:-$(tput cols)}"'}"}"'; echo
|
||||||
|
```
|
||||||
|
|
||||||
|
## The parameter expansion way
|
||||||
|
|
||||||
|
Preparing enough dashes in advance, we can then use a non-POSIX
|
||||||
|
subscript expansion:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
hr=---------------------------------------------------------------\
|
||||||
|
----------------------------------------------------------------
|
||||||
|
printf '%s\n' "${hr:0:${COLUMNS:-$(tput cols)}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
A more flexible approach, and also using modal terminal line-drawing
|
||||||
|
characters instead of hyphens:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
hr() {
|
||||||
|
local start=$'\e(0' end=$'\e(B' line='qqqqqqqqqqqqqqqq'
|
||||||
|
local cols=${COLUMNS:-$(tput cols)}
|
||||||
|
while ((${#line} < cols)); do line+="$line"; done
|
||||||
|
printf '%s%s%s\n' "$start" "${line:0:cols}" "$end"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related articles
|
||||||
|
|
||||||
|
- [printf](/commands/builtin/printf)
|
123
docs/snipplets/rndstr.md
Normal file
123
docs/snipplets/rndstr.md
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
# Print a random string or select random elements
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: terminal, line
|
||||||
|
LastUpdate_dt: 2013-04-30 Contributors: Dan Douglas (ormaaj) type:
|
||||||
|
snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
First off, here is a fast / reliable random string function for scripts
|
||||||
|
or libraries which can optionally assign directly to a variable.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Print or assign a random alphanumeric string of a given length.
|
||||||
|
# rndstr len [ var ]
|
||||||
|
function rndstr {
|
||||||
|
if [[ $FUNCNAME == "${FUNCNAME[1]}" ]]; then
|
||||||
|
unset -v a
|
||||||
|
printf "$@"
|
||||||
|
elif [[ $1 != +([[:digit:]]) ]]; then
|
||||||
|
return 1
|
||||||
|
elif (( $1 )); then
|
||||||
|
typeset -a a=({a..z} {A..Z} {0..9})
|
||||||
|
eval '${2:+"$FUNCNAME" -v} "${2:-printf}" -- %s "${a[RANDOM%'"${#a[@]}"']"{1..'"$1"'}"}"'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This example prints 10 random positional parameters and operates on
|
||||||
|
basically the same principle as the `rndstr` function above.
|
||||||
|
|
||||||
|
~ $ ( set -- foo bar baz bork; printf '%s ' "${!_[_=RANDOM%$#+1,0]"{0..10}"}"; echo )
|
||||||
|
bork bar baz baz foo baz baz baz baz baz bork
|
||||||
|
|
||||||
|
\<div hide\> This has some interesting option parsing concepts, but is
|
||||||
|
overly complex. This is a good example of working too hard to avoid an
|
||||||
|
eval for no benefit and some performance penalty. :/
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Print or assign a random alphanumeric string of a given length.
|
||||||
|
# rndstr [ -v var ] len
|
||||||
|
# Bash-only
|
||||||
|
rndstr()
|
||||||
|
if [[ $FUNCNAME == "${FUNCNAME[1]}" ]]; then
|
||||||
|
# On recursion, this branch unsets the outer scope's locals and assigns the result.
|
||||||
|
unset -v a b
|
||||||
|
printf -v "$1" %s "${@:2}"
|
||||||
|
elif ! { [[ $1 == -v ]] && shift; }; [[ $?+1 -ne $# || ${!#} != +([[:digit:]]) || ( $? -gt 0 && -z $1 ) ]]; then
|
||||||
|
# This branch does input validation, strips -v, and guarantees we're left with either 1 or 2 args.
|
||||||
|
return 1
|
||||||
|
elif (( ! ${!#} )); then
|
||||||
|
# If a zero-length string is requested, return success.
|
||||||
|
return
|
||||||
|
else
|
||||||
|
# This line generates the string and assigns it to "b".
|
||||||
|
local -a a=({a..z} {A..Z} {0..9}) 'b=("${a[RANDOM%'"${#a[@]}"']"{1..'"${!#}"'}"}")'
|
||||||
|
if (( $# == 2 )); then
|
||||||
|
# If -v, then pass a variable name and value to assign and recurse once.
|
||||||
|
"$FUNCNAME" "$1" "${b[@]}"
|
||||||
|
else
|
||||||
|
# If no -v, write to stdout.
|
||||||
|
printf %s "${b[@]}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
\</div\>
|
||||||
|
|
||||||
|
The remaining examples don\'t use quite the same tricks, which will
|
||||||
|
hopefully be explained elsewhere eventually. See
|
||||||
|
[unset](commands/builtin/unset#scope) for why doing assignments in this
|
||||||
|
way works well.
|
||||||
|
|
||||||
|
This next example is a variation on
|
||||||
|
[print_horizontal_line](/snipplets/print_horizontal_line). We\'re using
|
||||||
|
the printf field width specifier to truncate the values of a
|
||||||
|
`sequence expansion` to one character.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
a=({a..z} {A..Z} {0..9})
|
||||||
|
printf '%.1s' "${a[RANDOM%${#a[@]}]}"{0..9} $'\n'
|
||||||
|
```
|
||||||
|
|
||||||
|
The extra detail that makes this work is to notice that in Bash, [brace
|
||||||
|
expansion](syntax/expansion/brace) is usually the very first type of
|
||||||
|
expansion to be processed, always before parameter expansion. Bash is
|
||||||
|
unique in this respect \-- all other shells with a brace expansion
|
||||||
|
feature perform it almost last, just before pathname expansion. First
|
||||||
|
the sequence expansion generates ten parameters, then the parameters are
|
||||||
|
expanded left-to-right causing the [arithmetic](/syntax/arith_expr) for
|
||||||
|
each to be evaluated individually, resulting in independent selection of
|
||||||
|
random element of `a`. To get ten of the same element, put the array
|
||||||
|
selection inside the format string where it will only be evaluated once,
|
||||||
|
just like the dashed-line trick:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
printf "%.s${a[RANDOM%${#a[@]}]}" {0..9}
|
||||||
|
```
|
||||||
|
|
||||||
|
Selecting random elements whose lengths are not fixed is harder.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
a=(one two three four five six seven eight nine ten)
|
||||||
|
printf '%.*s ' $(printf '%s ' "${#a[x=RANDOM%${#a[@]}]} ${a[x]}"{1..10})
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates each parameter and it\'s length in pairs. The \'\*\'
|
||||||
|
modifier instructs printf to use the value preceding each parameter as
|
||||||
|
the field width. Note the space between the parameters. This example
|
||||||
|
unfortunately relies upon the unquoted command substitution to perform
|
||||||
|
unsafe wordsplitting so that the outer printf gets each argument. Values
|
||||||
|
in the array can\'t contain characters in IFS, or anything that might be
|
||||||
|
interpreted as a pattern without using `set -f`.
|
||||||
|
|
||||||
|
Lastly, empty brace expansions can be used which don\'t generate any
|
||||||
|
output that would need to be filtered. The disadvantage of course is
|
||||||
|
that you must construct the brace expansion syntax to add up to the
|
||||||
|
number of arguments to be generated, where the most optimal solution is
|
||||||
|
its set of prime factors.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
a=(one two three)
|
||||||
|
echo "${a[RANDOM%${#a[@]}]}"{,}{,,,,}
|
||||||
|
```
|
24
docs/snipplets/screen_saverestore.md
Normal file
24
docs/snipplets/screen_saverestore.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Save and restore terminal/screen content
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: terminal, restore
|
||||||
|
screen LastUpdate_dt: 2010-07-31 Contributors: Greg Wooledge type:
|
||||||
|
snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
This cool hack uses the terminal capabilities (see `terminfo(5)` manual)
|
||||||
|
**smcup** and **rmcup** to save and restore the terminal content.
|
||||||
|
|
||||||
|
For sure, you've already seen those programs that restore the terminal
|
||||||
|
contents after they did their work (like `vim`).
|
||||||
|
|
||||||
|
# save, clear screen
|
||||||
|
tput smcup
|
||||||
|
clear
|
||||||
|
|
||||||
|
# example "application" follows...
|
||||||
|
read -n1 -p "Press any key to continue..."
|
||||||
|
# example "application" ends here
|
||||||
|
|
||||||
|
# restore
|
||||||
|
tput rmcup
|
24
docs/snipplets/ssh_fetchkeys.md
Normal file
24
docs/snipplets/ssh_fetchkeys.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Fetching SSH hostkeys without interaction
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: ssh, ssh-keys
|
||||||
|
LastUpdate_dt: 2010-07-31 Contributors: Jan Schampera
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Applies at least to `openssh`.
|
||||||
|
|
||||||
|
To get the hostkeys for a server, and write them to `known_hosts`-file
|
||||||
|
(to avoid that yes/no query when the key isn\'t known), you can do:
|
||||||
|
|
||||||
|
ssh-keyscan -t rsa foo foo.example.com 1.2.3.4 >> ~/.ssh/known_host
|
||||||
|
|
||||||
|
This example queries the hostkeys for the very same machine, but under 3
|
||||||
|
different \"names\" (hostname, FQDN, IP) and redirects the output to the
|
||||||
|
`known_hosts`-file.
|
||||||
|
|
||||||
|
[**Notes:**]{.underline}
|
||||||
|
|
||||||
|
- if done blindly, the `known_host`-file may grow very large. It might
|
||||||
|
be wise to check for key existance first
|
||||||
|
- if multiple keys for the same host exist in `known_hosts`, the first
|
||||||
|
one is taken (which might be an old or wrong one)
|
17
docs/snipplets/ssh_local_var.md
Normal file
17
docs/snipplets/ssh_local_var.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Run some bash commands with SSH remotely using local variables
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: ssh, variables
|
||||||
|
LastUpdate_dt: 2010-07-31 Contributors: cweiss type: snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
In this example, we want to make sure a certain file exists on the
|
||||||
|
remote server:
|
||||||
|
|
||||||
|
file=/tmp/file.log
|
||||||
|
ssh ${options} ${login} "if [ ! -e '$file' ] ; then touch '$file' ; fi"
|
||||||
|
|
||||||
|
Notice the command is surrounded by double quotes, and the \$file
|
||||||
|
variable is surrounded by single quotes. That has the effect to be
|
||||||
|
wordsplit-proof in the local shell (due to the double-quotes) and in the
|
||||||
|
remote shell (due to the single-quotes).
|
93
docs/snipplets/wrapperargs.md
Normal file
93
docs/snipplets/wrapperargs.md
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# Generate code with own arguments properly quoted
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: arguments, quoting,
|
||||||
|
escaping, wrapper LastUpdate_dt: 2010-07-31 Contributors: Jan Schampera
|
||||||
|
type: snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Keywords: arguments,escape,quote,wrapper,generate
|
||||||
|
-------------- -----------------------------------------
|
||||||
|
Contributor: self
|
||||||
|
|
||||||
|
There are situations where Bash code needs to generate Bash code. A
|
||||||
|
script that writes out another script the user or cron may start, for
|
||||||
|
example.
|
||||||
|
|
||||||
|
The general issue is easy, just write out text to the file.
|
||||||
|
|
||||||
|
A specific detail of it is tricky: If the generated script needs to call
|
||||||
|
a command using the arguments the first original script got, you have
|
||||||
|
problem in writing out the correct code.
|
||||||
|
|
||||||
|
I.e. if you run your generator script like
|
||||||
|
|
||||||
|
./myscript "give me 'some' water"
|
||||||
|
|
||||||
|
then this script should generate code that looks like
|
||||||
|
|
||||||
|
echo give me 'some' water"
|
||||||
|
|
||||||
|
you need correct escapes or quotes to not generate shell special
|
||||||
|
characters out of normal text (like embedded dollar signs `$`).
|
||||||
|
|
||||||
|
**[Solution:]{.underline}**
|
||||||
|
|
||||||
|
A loop over the own arguments that writes out properly quoted/escaped
|
||||||
|
code to the generated script file
|
||||||
|
|
||||||
|
There are two (maybe more) easy options:
|
||||||
|
|
||||||
|
- writing out singlequoted strings and handle the embedded
|
||||||
|
singlequotes
|
||||||
|
- the [printf command](/commands/builtin/printf) knows the `%q` format
|
||||||
|
specification, which will print a string (like `%s` does), but with
|
||||||
|
all shell special characters escaped
|
||||||
|
|
||||||
|
## Using singlequoted string
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# first option:
|
||||||
|
# generate singlequoted strings out of your own arguments and handle embedded singlequotes
|
||||||
|
# here to call 'echo' in the generated script
|
||||||
|
|
||||||
|
{
|
||||||
|
printf "#!/bin/bash\n\n"
|
||||||
|
printf "echo "
|
||||||
|
for arg; do
|
||||||
|
arg=${arg/\'/\'\\\'\'}
|
||||||
|
printf "'%s' " "${arg}"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
} >s2
|
||||||
|
|
||||||
|
The generated script will look like:
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo 'fir$t' 'seco "ond"' 'thir'\''d'
|
||||||
|
|
||||||
|
## Using printf
|
||||||
|
|
||||||
|
The second method is easier, though more or less Bash-only (due to the
|
||||||
|
`%q` in printf):
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
{
|
||||||
|
printf "#!/bin/bash\n\n"
|
||||||
|
printf "echo "
|
||||||
|
for arg; do
|
||||||
|
printf '%q ' "$arg"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
|
} >s2
|
||||||
|
|
||||||
|
The generated script will look like:
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo fir\$t seco\ \"ond\" thir\'d
|
34
docs/snipplets/xclip.md
Normal file
34
docs/snipplets/xclip.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# X-Clipboard on Commandline
|
||||||
|
|
||||||
|
\-\-\-- dataentry snipplet \-\-\-- snipplet_tags: clipboard, x11, xclip,
|
||||||
|
readline LastUpdate_dt: 2010-07-31 Contributors: Josh Triplett type:
|
||||||
|
snipplet
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Make Control-v paste, if in X and if xclip available - Josh Triplett
|
||||||
|
if [ -n "$DISPLAY" ] && [ -x /usr/bin/xclip ] ; then
|
||||||
|
# Work around a bash bug: \C-@ does not work in a key binding
|
||||||
|
bind '"\C-x\C-m": set-mark'
|
||||||
|
# The '#' characters ensure that kill commands have text to work on; if
|
||||||
|
# not, this binding would malfunction at the start or end of a line.
|
||||||
|
bind 'Control-v: "#\C-b\C-k#\C-x\C-?\"$(xclip -o -selection c)\"\e\C-e\C-x\C-m\C-a\C-y\C-?\C-e\C-y\ey\C-x\C-x\C-d"'
|
||||||
|
fi
|
||||||
|
|
||||||
|
The behaviour is a bit tricky to explain:
|
||||||
|
|
||||||
|
- kill text after the cursor
|
||||||
|
- since the kill command **wants** text, it blindly adds a fake
|
||||||
|
text \"#\" here
|
||||||
|
- kill text before the cursor
|
||||||
|
- since the kill command **wants** text, it blindly adds a fake
|
||||||
|
text \"#\" here, too
|
||||||
|
- write out `"$(xclip -o -selection c)"`
|
||||||
|
- run Control-Meta-e (shell-expand-line) to expand the
|
||||||
|
`"$(xclip -o -selection c)"`
|
||||||
|
- yank the previously killed text back where it belongs
|
||||||
|
|
||||||
|
Of course you can use any other command, you\'re not limited to `xclip`
|
||||||
|
here.
|
||||||
|
|
||||||
|
Note: C-@ as well as M-SPC both works and set the mark for me \-- pgas
|
Loading…
Reference in New Issue
Block a user