- oneline commands
- sync mirror
- get all declare
- using string as variable name
<<<,< <(..)- parameter substitution
- string manipulations
- compound comparison
- echo
- ls
- tricky
oneline commands
[!TIP]
cat and EOF
[!NOTE|label:references:]
- Chapter 19. Here Documents
- bash Heredoc
- using heredoc with ssh
ssh -T user@host.com << EOF echo "The current local working directory is: $PWD" echo "The current remote working directory is: \$PWD" EOF- * POSIX.1 states
...an operand naming a file can be specified as '-', which means to use the standard input instead of a named file ....
kubectl apply from stdin
[!NOTE|label:references:]
$ cat << 'EOF' | kubectl apply -f - ... ... EOF # or $ kubectl apply -f - << EOF ... ... EOFgit apply from stdin
$ cat >> 'EOF' | git apply --inaccurate-eof --ignore-whitespace ... ... EOF # or $ git apply --inaccurate-eof --ignore-whitespace --stat << 'EOF' ... ... EOF install | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)git apply from clipboard
[!NOTE|label:references:]
$ pbpaste | git apply $ xsel --clipboard --input | git apply # or $ xclip -selection clipboard -o | git applypatch from stdin
[!NOTE|label:references:]
$ patch --dry-run --ignore-whitespace << 'EOF' ... ... EOF
while read from input
[!NOTE|label:references:]
$ while IFS=\| read -r col1 col2; do echo ">> $col1 .. $col2 <<"; done <<\INPUT
a|b
INPUT
>> a .. b <<
tail and timestamp
$ tail -f file | while read line; do echo -n $(date -u -Ins); echo -e "\t$line"; done
backup multiple files
$ cp -bfS.bak filename filename
ssh
compress and ssh and extract
$ tar cf - . | ssh elsewhere tar xf - -C /other/dir # or: https://www.commandlinefu.com/commands/view/4034/copy-a-folder-tree-through-ssh-using-compression-no-temporary-files $ ssh <host> 'tar -cz /<folder>/<subfolder>' | tar -xvztips
[!NOTE|label:references:]
# tips $ tar cfz - . | ssh otherhost "cd /mydir; tar xvzf -" # or: https://www.commandlinefu.com/commands/view/1629/pack-up-some-files-into-a-tarball-on-a-remote-server-without-writing-to-the-local-filesystem $ tar -czf - * | ssh example.com "cat > files.tar.gz" # the z-flag to tar does compression. Or you can use -C to ssh: $ tar cf - . | ssh -C otherhost "cd /mydir; tar xvf -"
find and tar
$ find . -name builds -prune -o -type f -print | tar czf ~/m.tar.gz --files-from -
# or find with maxdepth
$ find . -type f -name "config.xml" -maxdepth 2 -prune -print | tar czf ~/config.xml.130.tar.gz --files-from -
# find with special name
$ find . -name config\.xml -type f -print | tar czf ~/m.tar.gz --files-from -
# and ssh and extract
$ tar cf - . | ssh -C otherhost "cd /mydir; tar xvf -"
for JENKINS_HOME
backup all
config.xmlin JENKINS_HOME$ find ${JENKINS_HOME}/jobs \ -maxdepth 2 \ -name config\.xml \ -type f -print | tar czf ~/config.xml.tar.gz --files-from -back build history
$ find ${JENKINS_HOME}/jobs \ -name builds \ -prune -o \ -type f \ -print | tar czf ~/m.tar.gz --files-from -
-
$ find $PWD -maxdepth 1 -printf '%.5m %10M %#9u:%-9g %#5U:%-5G [%AD | %TD | %CD] [%Y] %p\n' # or $ find $PWD -maxdepth 1 -printf '%.5m %10M %#9u:%-9g %#5U:%-5G [%AD | %TD | %CD] [%Y] %p\n' | sort -rgbS 50% -
[!TIP|label:references:]
$ tar czv file1 file2 file3 | ssh user@host 'tar xzv -C /target/dir' recursively unrar into dir containing archive
$ find . -name '*.rar' -execdir unrar e {} \;
find and rename
$ find -iname "*.sh" -exec rename "s/.sh$/.shell/" {} \; -print
find and sort
via timestamp
[!NOTE]
- How to Find and Sort Files Based on Modification Date and Time in Linux
$ find . -type f -printf "\n%AD %AT %p" | head -n 11 | sort -k1.8n -k1.1nr -k1 # or $ find . -type f -printf "\n%Tm/%Td/%TY %TT %p" | sort -k1.8n -k1.1nr -k1 - * iMarslo : find printf
- *
printftime formats$ find . -printf "%T<format>\n" - sort by datetime format in bash
- how to sort file by DD-MM-YYYY dates
- Sort logs by date field in bash
# via %T+ $ find ~/.marslo -type f -printf "%10T+ | %p\n" | sort -r | head -10 # via %Td-%Tm-%TY $ find ~/.marslo -type f -printf "\n%Td-%Tm-%TY %TT %p" | sort -b -k1.7,1.10 -k1.4,1.5 -k1.1,1.2 -rvia awk+sort
$ cat a.txt ABC,1/02/2022,05:50 OPQ,18/10/2023,07:50 HIJ,31/09/2023,08:50 DEF,1/02/2021,06:00 $ awk -F'[,/]' '{printf "%04d-%02d-%02d\t%s\n", $4,$3,$2, $0}' a | sort -r | cut -f2- OPQ,18/10/2023,07:50 HIJ,31/09/2023,08:50 ABC,1/02/2022,05:50 DEF,1/02/2021,06:00
- How to Find and Sort Files Based on Modification Date and Time in Linux
find and copy
[!TIP]
$ find . -type f -newermt '2023-10-16 00:00:00' -exec cp -a --parents -t /path/to/target "{}" \+
# or
$ diff=$(( ($(date --date "24-02-29" +%s) - $(date --date "231016" +%s) )/(60*60*24) ))
$ find . -type f -daystart -mtime -$((diff+1)) -exec cp -a --parents -t /path/to/target "{}" \+
download and extract
[!TIP|label:references:]
gz
$ wget -O - http://example.com/a.gz | tar xz # or $ curl -fsSL http://example.com/a.gz | tar xz # or $ curl -fsSL "http://example.com/a.gz" | tar zxvf - # or: https://www.commandlinefu.com/commands/view/353/extract-tarball-from-internet-without-local-saving $ wget -qO - "http://www.tarball.com/tarball.gz" | tar zxvf -tar.gz
$ curl -fsSL https://dlcdn.apache.org/maven/maven-3/3.9.5/binaries/apache-maven-3.9.5-bin.tar.gz | tar xzf - -C /path/to/targeTtar.xz
$ curl -fsSL https://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.xz | tar -xJf - -C /path/to/targetzip
$ curl -fsSL https://downloads.gradle.org/distributions/gradle-8.4-bin.zip | bsdtar xzf - -C /path/to/target # with password $ curl -fsSL https://path/to/file.zip | bsdtar -xzf- --passphrase <PASSWD_OF_ZIP> - -C <EXTRACT_PATH>
mirror website
$ wget --mirror --page-requisites --html-extension --convert-links $URL
# https://www.linuxjournal.com/content/downloading-entire-web-site-wget
$ wget --recursive --no-clobber --page-requisites --html-extension --convert-links --restrict-file-names=windows --domains website.org --no-parent sample.com
# or download entire website: https://www.commandlinefu.com/commands/view/901/download-an-entire-website
$ wget --random-wait -r -p -e robots=off -U mozilla http://www.example.com
# https://superuser.com/a/1415765/112396
$ wget --recursive --level 5 --no-clobber --page-requisites --adjust-extension --span-hosts --convert-links --restrict-file-names=windows --domains sample.com --no-parent sample.com
# https://www.commandlinefu.com/commands/view/30/retrieve-a-list-of-all-webpages-on-a-site
$ URL=www.example.com && wget -rq --spider --force-html "http://$URL" && find $URL -type d > url-list.txt && rm -rf $URL
- download image only
$ wget -r -l1 --no-parent -nH -nd -P/tmp -A".gif,.jpg" http://example.com/images
kubectl apply from stdin
$ cat << EOF | kubectl create -f -
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: $(echo -n 'admin' | base64)
password: $(echo -n 'password' | base64)
EOF
show 256 colors
$ for code in {0..255}; do echo -e "\e[38;05;${code}m $code: Test"; done)
# or: https://www.commandlinefu.com/commands/view/6138/show-numerical-values-for-each-of-the-256-colors-in-bash
$ for i in {0..255}; do echo -e "\e[38;05;${i}m${i}"; done | column -c 80 -s ' '; echo -e "\e[m"
# or: https://www.commandlinefu.com/commands/view/11759/show-numerical-values-for-each-of-the-256-colors-in-bash-for-bold-and-normal-fonts
$ for code in $(seq -w 0 255); do for attr in 0 1; do printf "%s-%03s %bTest%b\n" "${attr}" "${code}" "\e[${attr};38;05;${code}m" "\e[m"; done; done | column -c $((COLUMNS*2))
# better version
$ for code in $(seq -w 0 255); do for attr in 0 1; do printf "%b%s-%03s%b\n" "\e[${attr};38;05;${code}m" "${attr}" "${code}" "\e[m"; done; done | column -c $((COLUMNS*3))
# .. zsh ..
# or: https://www.commandlinefu.com/commands/view/5876/show-numerical-values-for-each-of-the-256-colors-in-zsh
$ for code in {000..255}; do print -P -- "$code: %F{$code}Test%f"; done
# or: https://www.commandlinefu.com/commands/view/12471/show-numerical-values-for-each-of-the-256-colors-in-zsh
for i in {0..255}; do echo -e "\e[38;05;${i}m\\\e[38;05;${i}m"; done | column -c 80 -s ' '; echo -e "\e[m"
rm and exclude
$ rm !(*.foo|*.bar|*.baz)
# https://www.commandlinefu.com/commands/view/4576/remove-everything-except-that-file
$ find . ! -name <FILENAME> -delete
# https://www.commandlinefu.com/commands/view/4570/remove-everything-except-that-file
$ ( shopt -s extglob; rm !(<PATTERN>) )
split file to equal size
$ split -b4m file.tgz file.tgz. ; for i in file.tgz.*; do SUBJ="Backup Archive"; MSG="Archive File Attached"; echo $MSG | mutt -a $i -s $SUBJ YourEmail@(E)mail.com
sync mirror
[!NOTE]
$ rsync -aqzH --delay-updates --delete-after msync.centos.org::CentOS /path/to/local/mirror/root
# stream 9
$ rsync -aqzH --delay-updates --delete-after rsync.stream.centos.org::CentOS-Stream-All /path/to/local/mirror/root
# or src
$ rsync -aqzH --delay-updates --delete-after rsync.stream.centos.org::CentOS-Stream-nosrc /path/to/local/mirror/root
# exclude debuginfo
$ rsync -aqzH --delay-updates --delete-after rsync.stream.centos.org::CentOS-Stream-nodebug /path/to/local/mirror/root
get all declare
[!NOTE|label:references:]
$ declare
$ declare -p
$ declare -xp
# or
$ typeset
$ typeset -p
# or
$ compgen -v
$ compgen -v | while read line; do echo $line=${!line};done
$ compgen -v | while read line; do declare -p $line; done
# or
$ export
$ printenv
print env
[!NOTE|label:references:]
$ set -o posix ; set | awk -F '=' '{ print $1 }'
# or
$ env
$ env | awk -F '=' '{ print $1 }'
$ env | awk -F '=' '{ print $1 }' | tr '\n' ' '
# or
$ printenv
using string as variable name
[!NOTE|label:references:]
-
$ aa='echo me' $ var='aa' $ eval echo \$$var echo me -
$ var1="this is the real value" $ a="var1" $ echo "${!a}" this is the real value more usage
$ sunny=''' \033[38;5;226m \\ / \033[0m \033[38;5;226m .-. \033[0m \033[38;5;226m ― ( ) ― \033[0m \033[38;5;226m `-’ \033[0m \033[38;5;226m / \\ \033[0m ''' $ fewClouds=''' \033[38;5;226m \\ /\033[0m \033[38;5;226m _ /\"\"\033[38;5;250m.-. \033[0m \033[38;5;226m \\_\033[38;5;250m( ). \033[0m \033[38;5;226m /\033[38;5;250m(___(__) \033[0m ''' $ codeMap=( ["01"]="sunny" ["02"]="fewClouds" ) $ icon="$(/usr/bin/curl -sg "https://api.openweathermap.org/data/3.0/onecall?lat=37.3541132&lon=-121.955174&units=metric&exclude=hourly,daily,minutely,alerts&appid=${OWM_API_TOKEN}" | jq -r .current.weather[].icon)" $ echo ${icon} 02n $ echo -e "${!codeMap["${icon:0:-1}"]}"
1.2.1.2.1 -- using string as var name
<<<, < <(..)
[!TIP]
< <(is Process Substitution
- The difference between
<(...)and>(...)is merely which way the redirections are done
< <(..) && > >(..)
[!NOTE]
- process substitution
$ command1 < <( command2 ) # equals to $ command2 | command1 # if read from file, then using `< /path/to/file`example:
$ while read line; do echo "-- ${line} --"; done < <(ls -1) # equals to: http://mywiki.wooledge.org/BashFAQ/024 $ ls -1 | while read line; do echo "-- ${line} --"; done # equals to $ ls -1 | xargs -n1 -i bash -c "echo \"-- {} --\"" # equals to read from file via `< /path/to/file` $ ls -1 > ls.txt $ while read line; do echo "-- ${line} --"; done < ls.txt
$ wc < <(date)
1 6 29
# same as:
$ date | wc
1 6 29
< <(..)[!TIP|label:references:]
- subshell
tips:
# If commandA can read the data from stdin commandB | commandA # You can now get the exit code of commandB from PIPESTATUS. commandB > >(commandA) # You can now get the exit code of commandB from $? (or by putting this in an if) # If commandA cannot read it from stdin, but requires a file argument commandB > >(commandA <(cat)) # Again, commandB's exit code is available from $? # You can also keep commandB's output in memory. When you do this, you can get commandB's exit code from $? or put the assignment in an if b=$(commandB); commandA <<< "$b" # Here, commandA reads commandB's output from stdin
common usage
$ diff <(sort list1) <(sort list2) # or $ while read file; do echo -e "\n\033[1;33m${file}\n---\033[0m" sed -n "/<<<<<<< HEAD/,/>>>>>>> /!d;=;p" ${file} echo -e "\n\033[1;33m---\033[0m" done < <(git grep --no-color -l "<<<<<<< HEAD")
> >(..)[!TIP]
- Process Substitution
>(...)is used less frequently; the most common situation is in conjunction withtee(1).>(...)is handy when redirecting the output to multiple files, based on some criteria.
# For example: $ some_command | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null- Process Substitution
parameter substitution
| EXPR | DESCRIPTION |
|---|---|
${variable-default} |
if variable is unset, use default |
${variable=default} |
if variable is unset, set variable to default |
${variable+alt} |
if variable is set, use alt, else use null string |
${variable:-default} |
with ":[-=+]", condition takes also "declared but null" |
arguments substitution
| EXPR | DESCRIPTION |
|---|---|
$@ |
|
${@: 0} |
|
${@: 1} |
|
${@: 2} |
|
${@: 2:1} |
|
${@: 2:2} |
|
${@: -2} |
|
${@: -2:1} |
|
${*: -1} or ${@: $#} |
|
${@: 1:$#-1} |
|
sample with uncertain parameters
local opt='' local loop=true local path params while ${loop} && [[ $# -gt 0 ]]; do case "$1" in -*) opt+="$1 "; shift;; *) loop=false ;; esac done if [[ 1 = "$#" ]]; then path='' params="$1" else path=${*: -1} params=${*:1:$#-1} fiecho '---------------- before shift -------------------' echo ".. \$# : $#" echo ".. \$@ : $@" echo ".. \$* : $*" echo '---------------- after shift -------------------' opt='' ss='' loop=true while $loop && [[ $# -gt 0 ]]; do case "$1" in -*) opt+="$1 "; shift;; *) loop=false ;; esac done echo ".. \$# : $#" echo ".. \$@ : $@" echo ".. \$* : $*" echo ".. \$opt : $opt" if [[ 0 = "$#" ]]; then echo -e "\033[0;33mERROR: must provide at least one non-opt param\033[0m" exit 2 elif [[ 1 = "$#" ]]; then path='' params="$1" else path=${*: -1} params=${*:1:$#-1} fi echo '---------------- result -------------------' echo ">> opt : ${opt}" echo ">> params : ${params}" echo ">> path : ${path}"$ ./c.sh -1 -2 --3-4 a b c d e ---------------- before shift ------------------- .. $# : 8 .. $@ : -1 -2 --3-4 a b c d e .. $* : -1 -2 --3-4 a b c d e ---------------- after shift ------------------- .. $# : 5 .. $@ : a b c d e .. $* : a b c d e .. $opt : -1 -2 --3-4 .. $ss : a b c d e ---------------- result ------------------- >> opt : -1 -2 --3-4 >> params : a b c d >> path : e $ ./c.sh aa bb ---------------- before shift ------------------- .. $# : 2 .. $@ : aa bb .. $* : aa bb ---------------- after shift ------------------- .. $# : 2 .. $@ : aa bb .. $* : aa bb .. $opt : .. $ss : aa bb ---------------- result ------------------- >> opt : >> params : aa >> path : bb $ ./c.sh -1 ---------------- before shift ------------------- .. $# : 1 .. $@ : -1 .. $* : -1 ---------------- after shift ------------------- .. $# : 0 .. $@ : .. $* : .. $opt : -1 ERROR: must provide at least one non-opt param
quotas
${@@Q}# a.sh line="${@@Q}" echo $line $ bash a.sh -a -b --c='1 2 3' '-a' '-b' '--c=1 2 3'# https://stackoverflow.com/a/39463371/2940319 $ expand-q() { for i; do echo ${i@Q}; done; } $ expand-q -a -b --c='1 2 3' '-a' '-b' '--c=1 2 3'# https://stackoverflow.com/a/72745869/2940319 function quote() { local QUOTED_ARRAY=() for ARGUMENT; do case ${ARGUMENT} in --*=*) QUOTED_ARRAY+=( "${ARGUMENT%%=*}=$(printf "%q" "${ARGUMENT#*=}")" ) shift ;; *) QUOTED_ARRAY+=( "$(printf " %q" "${ARGUMENT}")" ) ;; esac done echo ${QUOTED_ARRAY[@]} } ARGUMENTS="$(quote "${@}")" echo "${ARGUMENTS}"printf " %q" "${@}"[!NOTE]
while test -n "$1"; do case "$1" in -- ) shift; GIT_OPT=$(printf " %q" "${@}"); break ;; esac done GIT_OPT="${GIT_OPT#\ \'\'}"# https://stackoverflow.com/a/39463371/2940319 $ params-q() { printf "%q\n" "$@"; } $ params-q -a -b --c='1 2 3' -a -b --c=1\ 2\ 3String replacement
[!TIP]
# a.sh while test -n "$1"; do case "$1" in -- ) shift; GIT_OPT="$@";; * ) shift;; esac done GIT_OPT=$(echo "${GIT_OPT}" | sed -r 's/\s+--/\n--/g' | sed -r "s/^([^=]+)=(.+)$/\1='\2'/g" | sed -e 'N;s/\n/ /' ) echo $GIT_OPT $ bash a.sh -a -b -- --c='1 2 3' --c='1 2 3'# https://stackoverflow.com/a/8723305/2940319 # a.sh C='' for i in "$@"; do i="${i//\\/\\\\}" C="$C \"${i//\"/\\\"}\"" done echo $C $ bash ~/a.sh -a -b --c='1 2 3' "-a" "-b" "--c=1 2 3"
string manipulations
reference:
| EXPR | DESCRIPTION |
|---|---|
${#string} |
length |
${string:position} |
substring, or positional parameter with $* and $# |
${string:position:length} |
substring |
${string#substring} |
deletes shortest match of $substring from front of $string |
${string##substring} |
same but longest match |
${string%substring} |
shortest from back |
${string%%substring} |
longest from back |
${string/substring/replacement} |
replace first match |
${string//substring/replacement} |
replace all matches |
${string/#substring/replacement} |
replace if matches front end of $string |
${string/%substring/replacement} |
replace if matches back end of $string |
${var^} |
uppercase first char |
${var^^} |
uppercase all chars |
${var,} |
lowercase first char |
${var,,} |
lowercase all chars |
compound comparison
SC2155
- problematic code:
([ "$x" ] || [ "$y" ]) && [ "$z" ] - correct code:
{ [ "$x" ] || [ "$y" ]; } && [ "$z" ] - example
SC2155
- problematic code:
export foo="$(mycmd)" - correct code:
foo="$(mycmd)" export foo
escape code
[!TIP] references:
| ESCAPE CODE | LANGUAGE | DESCRIPTION |
|---|---|---|
\x1b |
Node.js | hex char |
\x1b |
Node.js w/ TS | hex char |
\u001b |
Python | hex char |
\033 |
GNU Cpp | octal char |
\033 |
ANSI C | octal char |
\033 |
POSIX-compliant shells | octal char |
\e |
Bash | - |
\c[ |
- | control char |
echo
echo var name from variable
[!NOTE]
- Chapter 28. Indirect References
- 3.5.3 Shell Parameter Expansion:
{!parameter}- 9.2. Typing variables: declare or typeset
- sample:
$ foo=bar # `bar` is the var name $ bar=baz # `baz` is the var value
typeset$ typeset -p "${foo}"$ declare -- bar="baz"eval \$$$ eval echo \$$foo baz- more:
$ echo \$$foo $bar # or $ echo '$'$foo $bar
- more:
{!parameter}$ echo "${!foo}" baz
echo var name
[!NOTE]
- Shell: print both variable name and value?
- * How to Echo the Variable Name Instead of Variable Value
typeset -p- sample
$ a1='a' $ a2='aa' $ c1='c' $ c2='cc'
typeset$ typeset -p c2 declare -- c2="cc"{!parameter@}$ echo "${!c@}" c1 c2 $ echo "${!a@}" a1 a2more
superEcho() { echo "$1 = ${!1}" } $ superEcho foo foo = bar $ superEcho bar bar = baz
ls
[!NOTE|label:references]
# show file only
$ ls -p | command grep -v /
# show folder only
$ ls -p | command grep / | command grep "^."
# show all files ( including hidden )
$ ls -Ap | command grep -v / | command grep "^."
# show all folders ( including hidden )
$ ls -Ap | command grep / | command grep "^."
# show hidden folder only
$ ls -Ap | command grep / | command grep "^\." | command grep "\."
# show hidden file only
$ ls -Ap | command grep -v / | command grep "^\." | command grep "\."
# show all including hidden
$ ls -Ap
# show hidden file and folder
$ ls -Ap | command grep "^\."
tricky
alias for sudo
[!TIP|label:references:]
alias sudo='sudo '
get md5sum
get file in tar without exacting
$ tar -O -xf file.tar.gz file.txt | md5sum # or $ tar xfO file.tar.gz file.txt | md5sumget file in zip without extracting
$ unzip -p file.zip file.txt | md5sum
env
HISTTIMEFORMAT
# https://www.commandlinefu.com/commands/view/3642/save-date-and-time-for-each-command-in-history export HISTTIMEFORMAT="%h/%d-%H:%M:%S " # or: YYYY-MM-DD HH:MM:SS : https://www.commandlinefu.com/commands/view/3646/save-date-and-time-for-each-command-in-history $ export HISTTIMEFORMAT='%F %T '-
$ GREP_OPTIONS='-D skip --binary-files=without-match --ignore-case'
shortcuts
- inserts the results of an autocompletion in the command line
$ <command> <kbd>esc</kbd> <kbd>*</kbd>- i.e.:
$ echo |-> esc *
- i.e.:
man
-
$ man hier show ascii
$ man ascii-
$ showkey -a