mirror of
https://github.com/flokoe/bash-hackers-wiki.git
synced 2024-11-24 23:33:41 +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