|
home /
infca /
linux /
lshell
(navigation links)
|
Ned - no matter what you think, i do love you
|
Linux shell code
go 2 top
shells
A Unix shell is a command-line interpreter or shell
that provides a command line user interface for Unix-like operating systems.
The shell is both an interactive command language and a scripting language,
and is used by the operating system to control the execution of the system using shell scripts.
Shell scripting can be defined as a group of commands executed in sequence.
- tipus de shell :
- bash - free version of the Bourne shell, with many additional features.
- csh - C shell uses a "C-like" syntax.
- ksh - Korn shell uses the same syntax as the Bourne shell and has included the uses-fiendly features of the C shell.
- tzsh - the TC shell is an enhanced version of the C shell, and is 100 % compatible with it.
- zsh - the Z shell is an enhanced version of the Korn shell, with many features found in the bash shell.
- psh, or perl shell
- ash - very lightweight, uses a language common to both interactive and non-interactive use.
- diferencies entre el bash i altres :
aquí
(C1) - bash versus sh (Bourne shell)
(C2) - bash versus Korn shell
- com saber quin SHELL tens / fas servir ? "which $SHELL"
Per exemple, per fer un grep per un string_buscat en tots els
fitxers *.c del directori actual i subdirectoris, podem fer :
- find -name '*.c' -exec grep -H string_buscat {} \;
- grep string_buscat `find . -name \*.c`
- find . -name '*.c' | xargs grep 'string_buscat'
- grep -r string_buscat *.c .
exemple minim
#!/bin/bash
# Autor : jo
myLog="/home/mate/logs/mylog.log" # log file
echo "+++ [`date -R`] +++ ("$0") +++" >> $myLog # ident by prog name
exit 0
Shell bits
- it's good practice not to use all-uppercase names for shell variables,
so as to avoid conflicts with environment variables (PAGER, EDITOR, ...) and internal shell variables (SHELL, BASH_VERSION, ...)
- indent 2 spaces, no tabs
- in bash a semi-colon is a statement separator, not a statement terminator, which is a new-line
- comentaris : comencen per # i s'ignora fins a final de linia
You can also have comments that span multiple lines by using a colon and single quotes (')
Style,
tips
shell variables concatenation
Do it joining their names :
fqDest="/media/sebas/"${myParam}
shell variables hints
We can access the value of the variable using the dollar sign ($):
$ A=2
$ echo $A
2
We can additionally use curly brackets to separate the variable’s name from the rest of the expression:
$ echo ${A}string
2string
To quote or not to quote
Always quote variable expansions even when you know the value won't contain any IFS characters
This makes the contents of the variable an atomic unit.
If the variable value contains blanks (well, characters in the $IFS special variable, blanks by default)
or globbing characters and you don't quote it,
then each word is considered for filename generation (globbing)
whose expansion makes as many arguments to whatever you're doing.
stackExchange
EX_VARIABLE="24/22/2021"
SQL_MSG=`sqlplus -s <user>/<pwd>@<db> <<-EOF
whenever SQLERROR EXIT SQL.SQLCODE ROLLBACK;
whenever OSERROR EXIT SQL.SQLCODE ROLLBACK;
select to_date('${EX_VARIABLE}','dd/mm/yyyy') from dual;
EOF`
if [ $? -eq 0 ]; then
echo "OK";
else
echo "${SQL_MSG}" # very important quotes here
fi
Using curling parenthesis in var names
both $VAR and ${VAR} are used to access the value of a variable
And in many cases, they work the same. But ${VAR} is more versatile and safer in certain situations.
Use ${VAR} when:
- you want to append characters immediately after the variable name :
FILE="data"
echo "$FILE.txt" # => might confuse bash
echo "${FILE}.txt" # ✅ safer and correct
- you're using string manipulation or parameter expansion :
echo "${NAME^^}" # => "WORLD" (uppercase)
echo "${NAME,,}" # => "world" (lowercase)
echo "${NAME:0:3}" # => "wor" (substring)
- you're providing default values or doing null checks :
echo "${USER_NAME:-guest}" # use "guest" if USER_NAME is unset or empty
😎 in doubt — just go with ${VAR} 😎
shell init file sequence(s)
- SuSE 7.2
- ~/.bashrc
- /etc/profile
- /etc/profile.local
- Caldera 2.4
- ~/.profile
- ~/.profile-private
bash files
- /etc/bash.bashrc - system-wide .bashrc file for interactive bash(1) shells
- /etc/profile - the systemwide initialization file, executed for login shells -
system-wide .profile file for the Bourne shell
- /etc/bash.bash_logout - the systemwide login shell cleanup file, executed when a login shell exits
- ~/.bash_profile - the personal initialization file, executed for login shells
- ~/.bash_aliases - private aliases, called from .bashrc
- ~/.bashrc - the individual per-interactive-shell startup file -
executed by bash(1) for non-login shells
- ~/.bash_logout - the individual login shell cleanup file, executed when a login shell exits
- ~/.inputrc - individual readline library initialization file
- ~/.profile: executed by the command interpreter for login shells
reload profile without "logoff"
Do ". ~/.profile" (mind the initial dot followed by a space)
pi@R4:~ $ . ~/.profile
+++ .bash_aliases - v1.1a 20210518
--- .bash_aliases - v1.1a 20210518
pi@R4:~ $
what is a login or non-login shell?
When you login (type username and password) via console,
either sitting at the machine, or remotely via ssh,
.bash_profile is executed to configure your shell before the initial command prompt.
But, if you’ve already logged into your machine and open a new terminal window (xterm) inside Gnome or KDE,
then .bashrc is executed before the window command prompt.
.bashrc is also run when you start a new bash instance by typing /bin/bash in a terminal
PATH
PATH is system-wide set at /etc/profile
User can modify it :
# set PATH so it includes user's private bin directories
PATH="$HOME/bin:$HOME/.local/bin:$PATH"
On Ubuntu we place this line in "~/.profile" or "~/.bashrc" (better)
She-Bang : #!
The first line of a shell file tells the computer how to interpret this script.
Un classic de BASH :
#!/bin/bash
myLog="/home/mate/logs/mylog.log"
echo "+++ [`date -R`] +++ ("$0") +++" >> $myLog
exit 0
Un exemple de Perl :
#!/usr/bin/perl
print "Look at all the camels!\n";
Un exemple de Bourne Shell :
#!/bin/sh
echo "El Path es" $PATH
echo "El Path es" ${PATH}
echo "[`date -R`] - Different Hash, New Passwd File";
# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
printf "My IP address is %s\n" "$_IP"
fi
Un exemple de Rexx :
#!/usr/bin/rexx
say Date() Time() "- bondia."
parse version MyVersion
say "La versio de Rexx es {" || MyVersion || "}."
Portability
You better use
#!/usr/bin/env bash
Or for whatever the language such as for Perl
#!/usr/bin/env perl
Id
To identify in a log file who is writing into it, we shall use
echo "+++ [`date -R`] +++ ("$0") +++" >> $mylog
We can use this line to "init" the logfile ...
built-in commands
bash reference manual
echo versus printf
printf has more formatting options
The difference is that echo sends a newline at the end of its output.
- echo [-neE] [arg …]
Output the args, separated by spaces, terminated with a newline.
The return status is 0 unless a write error occurs.
If -n is specified, the trailing newline is suppressed.
If the -e option is given, interpretation of the following backslash-escaped characters is enabled.
The -E option disables the interpretation of these escape characters, even on systems where they are interpreted by default.
- printf [-v var] format [arguments]
Write the formatted arguments to the standard output under the control of the format.
The -v option causes the output to be assigned to the variable var rather than being printed to the standard output.
bash shell functions
A bash function is essentially a set of commands that can be called numerous times.
bash shell function sample
function_name () {
sz_NomFitxer=$1 # get first param
sz_File_Size=$2 # get second param
# some commands
myRC=0
return $myRC # to make this clear: return (like exit) can only take a value from 0-255
}
linux.ize
Call your function without parenthesis neither commas :
iRC=function_name $myFileName $myFileSize
bash shell function returning values
Bash functions, unlike functions in most programming languages, do not allow you to return a value to the caller.
When a bash function ends its return value is its status: zero for success, non-zero for failure.
To return values, you can set a global variable with the result, or use command substitution, or you can pass in the name of a variable to use as the result variable.
The simplest way to return a value from a bash function is to just set a global variable to the result.
Since all variables in bash are global by default this is easy:
function myfunc()
{
myresult='some value'
}
myfunc
echo $myresult
The other way to return a value is to write your function so that it accepts a variable name as part of its command line
and then set that variable to the result of the function:
function myfunc()
{
local __resultvar=$1
local myresult='some value'
eval $__resultvar="'$myresult'"
}
myfunc result
echo $result
linux journal
This is my way :
nicolau@mars:~/eines/proves$ cat 3_prova_return_from_function.sh
#!/bin/bash
function la_meva_funcio {
echo "+++ funcio, param ("$1")."
let myRC=$1+5
return $myRC
}
echo "anem"
la_meva_funcio 22
rv=$?
echo "tornem" $rv
exit 0
El resultat :
nicolau@mars:~/eines/proves$ ./3_prova_return_from_function.sh
anem
+++ funcio, param (22).
tornem 27
bash shell parameters
pi@odin:~/eines $ cat 2_mostra_params.sh
#!/bin/bash
echo Script name: $0
echo We have $# arguments
j=0
for i; do
let j=j+1
echo 'Parameter '$j' is ' $i
done
A simple sample - "sh userReg-positional-parameter.sh john 25 'John Smith'"
echo "Username: $1";
echo "Age: $2";
echo "Full Name: $3";
A more complete, with flags - "sh userReg-flags.sh -f 'John Smith' -a 25 -u john"
while getopts u:a:f: flag
do
case "${flag}" in
u) username=${OPTARG};;
a) age=${OPTARG};;
f) fullname=${OPTARG};;
esac
done
echo "Username: $username";
echo "Age: $age";
echo "Full Name: $fullname";
Special parameters
Llegim aqui :
- "$@" expands into a list of separate parameters.
- "$*" is one parameter consisting of all the parameters added together.
- "$#"" is the special parameter in bash which gives you the number of positional parameter in decimal.
- The special parameter "$$"" will give the process ID of the shell.
- "$!" gives you the process id of the most recently executed background process.
- "$?" gives the exit status of the most recently executed command.
- "$_" gives the last argument to the previous command. At the shell startup, it gives the absolute filename of the shell script being executed
- "$-" options set using set built-in command
Com passar un string com 1 sol parametre
El truc es canviar "IFS", el "internal field separator" :
export IFS='-' # canviem el Internal Field Separator per poder passar string com a parametre
# define possible commands we can use :
sz_CMD_IPS='ip ; neighbor ; print detail' # nodes conectats a un super-node
sz_CMD_FW_USR='ip ; firewall ; address-list ; print detail' # usuaris que tenen sortida a la Fibra Optica
sz_PXY_USERS='ip ; proxy ; access ; print detail' # usuaris privilegiats del Web Proxy, o sigui, amb acces a 172.xxx
function accedir { # +++ define function
sz_SERVIDOR=$1
sz_COMMAND=$2
sshpass -p "my_pwd" ssh -o StrictHostKeyChecking=no $my_USER@$sz_SERVIDOR $sz_COMMAND
return 0
} # --- define function
TaborST1AP1=10.139.130.129
TaborST1AP3=10.139.238.129
TaborRD1AP1=10.139.238.225
TaborRD1AP2=10.139.239.65
TaborRD1AP3=10.139.238.97
CampanarST1AP1=10.139.130.65
CementiriAP1=10.139.130.161
CanCollAP1=10.139.130.193
accedir $CampanarST1AP1 $sz_PXY_USERS
exit 0
Subroutines
mate@punt-omnia:~/eines/ssh$ cat scan.sh
#!/bin/bash
# https://gist.github.com/arunoda/7790979 - utility which allows you to provide the ssh password without using the prompt
MY_USER="usuari"
FN_O="out.txt"
# erase old output file
echo ' ' > $FN_O
function accedir { # +++ define function
SERVIDOR=$1
szMSG="+++ +++ [`date -R`] +++ +++ Accedir al server {"$SERVIDOR"}."
echo $szMSG >> $FN_O
sshpass -p "my.pwd" ssh -o StrictHostKeyChecking=no $MY_USER@$SERVIDOR 'ip ; neighbor ; print detail' >> $FN_O
ls -al $FN_O
return 0
} # --- define function
TaborST1AP1=10.139.130.129
CanCollAP1=10.139.130.193
accedir $TaborST1AP1
accedir $CanCollAP1
exit 0
jobs in background
"jobs -l" or "jobs -p" show background processes only for current session
nohup,
huponexit
"nohup" versus "ampersand"
From bash manpage :
If a command is terminated by the control operator &, the shell executes the command in the background in a subshell.
The shell does not wait for the command to finish, and the return status is 0.
From nohup manpage :
nohup - run a command immune to hangups, with output to a non-tty
nohup catches the hangup signal while the ampersand does not.
What is the hangup signal? SIGHUP - hangup detected on controlling terminal or death of controlling process (value: 1).
There is no difference between these launching methods when the shell is configured in a way where it does not send SIGHUP at all.
In case you are using bash, you can use the command specified below to find out whether your shell sends SIGHUP to its child processes or not:
~ $ shopt | grep hupon
script versioning
To identify the version of the script we are using, we have to use :
#!/bin/bash
# v 1.m - use mutt
myVer="1.m"
szME="+++ [$myTm] +++ [$HOSTNAME] +++ [$(whoami)] +++ [$0] +++ v ($myVer) +++"
echo -e "$szME" >> $myLog 2>&1
bash conditional expressions
See manual,
samples (binary, string, integer) ;
We can test of : files, strings and numbers
Logical operators are : and, or and not
Expressions may be combined using the following operators, listed in decreasing order of precedence - see "man bash"
( expression )
Returns the value of expression. This may be used to override the normal precedence of operators.
! expression
True if expression is false.
expression1 && expression2
True if both expression1 and expression2 are true.
expression1 || expression2
True if either expression1 or expression2 is true.
The opening bracket [ is actually a command :
$ which [
/usr/bin/[
numeric comparison operators
Possible comparacions :
arg1 -eq arg2 True if arg1 equals arg2
arg1 -ne arg2 True if arg1 is not equal to arg2
arg1 -lt arg2 True if arg1 is less than arg2
arg1 -le arg2 True if arg1 is less than or equal to arg2
arg1 -gt arg2 True if arg1 is greater than arg2
arg1 -ge arg2 True if arg1 is greater than or equal to arg2
Exemple (one-liner) :
$ X=1 ; if [ $X -eq 1 ] ; then echo "X equals 1" ; else echo "X does not equal 1" ; fi
X equals 1
opensource
String comparison operators
-z string True if the length of string is zero
-n string True if the length of string is non-zero
string1 == string2 True if the strings are equal; When used with the [[ command, this performs pattern matching as described above (compound commands).
string1 = string2 True if the strings are equal; a single = should be used with the test command for POSIX conformance.
string1 != string2 True if the strings are not equal
string1 < string2 True if string1 sorts before string2 lexicographically (refers to locale-specific sorting sequences for all alphanumeric and special characters)
string1 > string2 True if string1 sorts after string2 lexicographically
logical operations on files
-a file True if file exists
-e file True if file exists
-d file True if file exists and is a directory
-x file True if file exists and is executable
-s file True if file exists and has a size greater than zero ;
tecadmin
-f file True if file exists and is a regular file
-n string True if the length of string is non-zero
-z string True if the length of string is zero
-eq True if EQUAL
-ne True if not equal
double conditions
We can test in 2 steps :
#!/usr/bin/env bash
FILENAME=myfile.txt
if [ -f "${FILENAME}" ]; then
if [ -s "${FILENAME}" ]; then
echo "File ${FILENAME} exists and not empty"
else
echo "File ${FILENAME} exists but empty"
fi
else
echo "File ${FILENAME} not exists"
fi
... or both at the same time :
echo "make a choice 1"
read choice1
echo "make a choice 2"
read choice2
if [ "$choice1" == 'y' ] && [ "$choice2" == 'y' ]; then
echo "Test Done !"
else
echo "Test Failed !"
fi
Here is a nice one :
#!/bin/bash
read x
if [ $x == "Y" ] || [ $x == "y" ]
then
echo "YES"
else
echo "NO"
fi
Verify return code
$? expands to the exit status of the most recently executed foreground pipeline.
See the
Special Parameters
section of the Bash manual.
rv=$?
echo ">>> my return code is" $rv >> $myLog
if [ "$rv" -ne 0 ]; then
echo "--- mira errors a /var/log/syslog" >> $myLog
fi
Verify I am root
#!/bin/ksh
if [ `id -u` -ne 0 ]
then
echo "Must be running as root"
exit 1
fi
Verify a file exists
#!/bin/bash
FILE=/etc/resolv.conf
if [ -f "$FILE" ]; then
echo "$FILE exist"
else
echo "$FILE does not exist"
fi
If we want to exit if file does not exist :
#!/bin/bash
FILE=/etc/resolv.conf
if [[ ! -f $FILE ]]
then
echo "$FILE does not exist on your filesystem."
fi
Verify I have a parameter
#!/bin/bash
if [ -z "$1" ]
then
echo "--- No argument supplied"
exit 1
fi
To modify default value :
#!/bin/bash
fn="default.value"
if [ -n "$1" ]
then
fn=$1
fi
echo "Filename" $fn
nested if conditions
nicolau@mars:~/sebas/python/bot$ cat post_2_bot.sh
iSel=$1
echo "Seleccio" $iSel
if [[ iSel -eq 0 ]]
then
echo FER ZERO
else
if [[ iSel -eq 1 ]]
then
echo FER U
else
echo fer resta
fi
fi
double if conditions
The comparison operator "-ne" is an arithmetic operator, i.e. it compares only integers:
i=7
if [ "$i" -ne 6 ] && [ "$i" -ne 8 ]; then
echo 'i is neither 6 nor 8'
fi
To compare strings for inequality, use !=:
if [ "$filename" != 'even' ] && [ "$filename" != 'odd' ]; then
printf '%s\n' "$filename"
fi
create a large string + display string length
nicolau@mars:~/eines/proves$ cat 4_prova_allargar_string.sh
#!/bin/bash
myVar="a"
iCnt=1
while [ $iCnt -le 12 ]; do # 2 exp 12 = 4096
echo "The counter is" $iCnt
myVar=$myVar$myVar
let iCnt=$iCnt+1
done
mySize=${#myVar} # use “#” symbol to get string length
echo "("$mySize")."
echo "("$myVar")."
exit 0
Envir vars
How to set global envir vars
sebas@r4:~ $ cat /etc/environment
SAG_TUSR="t_user"
SAG_TPWD="t_pwd"
Compte : no hi pot haver comentaris en aquest fitxer, ni res despres de les cometes de tancar
Few envir values we can use
Use env command to display available ones ...
#!/bin/bash
myhost="9.137.164.25"
myqos="0"
mytopic="bisc/monit/pcs"
myid="mosquitto_pubTASK_client"
myuser=$USER
mymsgid="bisc.TT2.mpc.v1a"
myhn=$HOSTNAME
myip="$(/sbin/ifconfig eth0 | grep 'inet addr:9' | cut -d: -f2 | awk '{ print $1}')"
mydate=`date +"y%Y/m%m/d%d"`
mytime=`date +"h%H:m%M"`
mymsg="$mymsgid $myhn $myip $mydate $mytime"
echo "MyMsg={" $mymsg "}."
Verify a directory exists
if [ -d "$LINK_OR_DIR" ]; then # use "!" to change it to "not exist"
if [ -L "$LINK_OR_DIR" ]; then
# It is a symlink!
# Symbolic link specific commands go here
rm "$LINK_OR_DIR"
else
# It's a directory!
# Directory command goes here
rmdir "$LINK_OR_DIR"
fi
fi
url
Verify a file exists
if [ -e "$FN" ]; then # use "!" to change it to "not exist"
BASH intro to
IF.
File test operators :
url.
Generate a timestamp - multiple date() forms
Bona url
sebas@T60ubuntu:~/eines/prova_shell$ cat dates.sh
#!/bin/bash
# input : $1 = directory name
myT=$(date +%R) # short time (20:27,01/07/19) - no posa els segons
myD=$(date +%D) # short date (20:27,01/07/19) - posa any-dia-mes
myT=$(date +"%H:%M:%S") #
myT=$(date +"%H:%M:%S%N") # ... microseconds
myT=$(date +"%H:%M:%S%3N") # ... miliseconds
myD=$(date +"%d/%m/%Y") # short date, 4 digit year, day before month
myZ=$(date +%Z) # timezone
myDate=`date +"Y%y/M%m/D%d"`
myTime=`date +"h%H:m%M:s%S"`
tz_SAG="Europe/Madrid" # see /usr/share/zoneinfo, as "America/Los_Angeles"
bak_file=$myDate".tgz"
dir_dst="/vmfs/volumes/Storage1/backups"
fn1=$1"_"$bak_file
f_dst=$dir_dst"/"$fn1
echo -e "("$myDate"-"$myTime") +++ ($myT,$myD,$myZ) +++ [$(date '+%Y %b %d %H:%M')] +++\n+++ [`date -R`] +++ "$(TZ=$tz_SAG date)" +++\n+++ Crear {"$f_dst"}"
exit 0
La sortida es
sebas@T60ubuntu:~/eines/prova_shell$ ./dates.sh potato
(Y19/M01/D07-h20:m27) +++ (20:27,01/07/19,CET) +++ [2019 ene 07 20:27] +++
+++ [Mon, 07 Jan 2019 20:27:05 +0100] +++ lun ene 7 20:27:05 CET 2019 +++
+++ Crear {/vmfs/volumes/Storage1/backups/potato_Y19/M01/D07.tgz}
Best timestamp
myT=$(date +"%H:%M:%S") #
myD=$(date +"%d/%m/%Y") # short date, 4 digit year, day before month
myStamp=$myD"_"$myT
echo ">>> Timestamp is *" $myStamp "*"
exit 0
user-function : timestamp
my_timestamp()
{
date +"%H:%M:%S"
}
Later use it :
RC=$?
echo ">>> $(my_timestamp) wget rc = ("$RC")."
Few commands in one line
Combining two or more commands on the command line is also known as “command chaining”.
It can be done in different ways :
- the semicolon (;) operator allows you to execute multiple commands in succession, regardless of whether each previous command succeeds.
- if you want the second command to only run if the first command is successful,
separate the commands with the logical AND operator, which is two ampersands ( && )
- if you want to execute a second command only if the first command does not succeed,
use the logical OR operator, or two vertical bars ( || ).
url
Sample :
guifi@torrelles:/etc/cron.d$ cat dnsservices
#
# Regular cron jobs for the dnsservices package
#
*/30 * * * * root cd /etc/bind && /usr/bin/php /usr/share/dnsservices/dnsservices.php >> /var/log/dnsservices/dnsservices.log && /etc/init.d/bind9 reload
Redirecting Standard Output and Standard Error
See "man bash"
discard all output
The command is :
sebas@pi0alby:~ $ nohup sudo ping 192.168.1.2 &> /dev/null &
[1] 15773
stdout and stderr to same file
The &> myfilename construct allows both the standard output (file descriptor 1) and the standard error output (file descriptor 2)
to be redirected to the file whose name is the expansion of "myfilename".
This is semantically equivalent to "1> myfilename 2>&1"
Appending ***all***
The format for appending standard output and standard error is:
&>> filename
1>> filename 2>&1 # molt millor, per no tenir problemes amb el "&" (go background)
This is semantically equivalent to
>> filename 2>&1
my redirections
guifi@torrelles:/etc/cron.d$ cat guifi-proxy
#
# Regular cron jobs for the guifi-proxy package
#
55 * * * * root /usr/share/guifi-proxy/guifi-proxy.sh >> /var/log/guifi-proxy/guifi-proxy.log 2>&1;
url
why it is 2>&1 and not 2>>&1
Lets try
$ curl www.google.com 1> a.txt 2> b.txt
Take a look on "error" output from curl, written into b.txt :
sag@odin:/tmp/sag $ cat b.txt
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 12469 0 12469 0 0 116k 0 --:--:-- --:--:-- --:--:-- 115k
So, we always have output on stderr ... Now we do
sag@odin:/tmp/sag $ rm a.txt
sag@odin:/tmp/sag $ echo "##sag##" >> a.txt && curl www.google.com 1>> a.txt 2>&1
sag@odin:/tmp/sag $ echo "##sag##" >> a.txt && curl www.google.com 1>> a.txt 2>&1
sag@odin:/tmp/sag $ echo "##sag##" >> a.txt && curl www.google.com 1>> a.txt 2>&1
sag@odin:/tmp/sag $ echo "##sag##" >> a.txt && curl www.google.com 1>> a.txt 2>&1
sag@odin:/tmp/sag $ cat a.txt | grep '##sag##' | wc
4 7 578
So, "2>&1" does not mean "replace" but "place stderr where stdout goes"
TEE
Use the
tee command to display on screen and simultaneously redirect to file !
heredoc
To write to a file an explicit text, use "heredoc" :
#!/bin/sh
FILE="/path/to/file"
/bin/cat <<EOM >$FILE
text1
text2 # This comment will be inside of the file.
The keyword EOM can be any text, but it must start the line and be alone.
text4
EOM
Concepts I have missing
How to know own directory when running a shell from crontab ?
Problem : if the script writes a file and is executed from crontab, the file is written into /home/<user>
Solution :
cd "$(dirname "$0")";
Temporary files
Often, there will be times when you need to create a temporary file.
Once the program's purpose is completed, the file is often deleted.
When you create a file, you have to give it a name.
The problem is, the file you create, must not already existing in the directory you are creating it in.
Otherwise, you could overwrite important data.
In order to create a unique named temporary file, you need to use the "$$" symbol,
by either prefixing, or suffixing it to the end of the file name.
Take for example, you want to create a temporary file with the name hello.
Now there is a chance that the user who runs your program, may have a file called hello,
so that would clash with your program's temporary file.
By creating a file called hello.$$, or $$hello instead, you will create a unique file.
Try it:
xconsole$ touch hello
xconsole$ ls
hello
xconsole$ touch hello.$$
xconsole$ ls
hello hello.689
There it is, your temporary file.
Verify return code from a MQ program
#!/bin/bash
fn="dades.txt"
./myget QL.SIT P9111
rc=$?
if [ $rc -eq 0 ]
then echo "+++ rc eq 0 : exit status no messages."
else echo "+++ rc ne 0 : exit status ($rc). There are messages in the queue."
fi
Compte !
The return from main() is handed to exit(). From "man 3 exit" (macOSX, but other UNIX are essentially the same):
[...]
void exit(int status);
[...]
make the low-order eight bits of the status argument available to a parent process which has called a wait(2)-family function.
group unix-basics
Multiply in bash
MSGLNG=$(expr 10 \* 1024)
echo "... each " $MSGLNG " bytes long."
An alternative to using expr, is to enclose the arithmetic operation inside $((...)).
This is different from $(...).
The number of brackets will tell you that. Let us try it:
#!/bin/bash
x=8 # initialize x to 8
y=4 # initialize y to 4
# now we assign the sum of x and y to z:
z=$(($x + $y))
echo "The sum of $x + $y is $z"
Searching for a substring
First try :
#!/bin/bash
if [[ "$sz_IP" =~ "172." ]]; then
echo "IP ("$sz_IP") is internal"
else
echo "IP ("$sz_IP") is public"
fi
If we have a line like
#90# Darrera generacio : {2020-09-05 14:00:05}.
The we can do
url :
szDate=$( cat IP_PI0.HTM | grep "#90#" | cut -d'{' -f 2 | cut -d'}' -f 1 )
echo ">>> Last date" $szDate
Reading user input
#!/bin/bash
# gets the name of the user and prints a greeting
echo -n "Enter your name: "
read user_name
# the user did not enter anything:
if [ -z "$user_name" ]; then
echo "You did not tell me your name!"
exit
fi
echo "Hello $user_name!"
incrementing a global var
#!/bin/bash
iCNT=0
function accedir {
iCNT=$(($iCNT+1))
szURL=$1
echo "($iCNT) URL { $szURL }"
return 0
}
accedir https://my.net
accedir https://pi.ink
exit 0
Virgueries grafiques
Display all box-drawing chars
#!/bin/bash
char=( 6a 6b 6c 6d 6e 71 74 75 76 77 78 )
for i in ${char[*]}
do
printf "0x$i \x$i \e(0\x$i\e(B\n"
done
Resultat :
sebas@minie:~/dades/bash$ ./box_chars.sh
0x6a j ┘
0x6b k ┐
0x6c l ┌
0x6d m └
0x6e n ┼
0x71 q ─
0x74 t ├
0x75 u ┤
0x76 v ┴
0x77 w ┬
0x78 x │
url
Caracters especials - backslash escapes
#!/bin/bash
echo -e "\n +++ [`date -R`] \t is ..." ; "-e" means "allow backslash escapes"
Colorejar el texte
ANSI codes :
Black 0;30 Dark Gray 1;30
Red 0;31 Light Red 1;31
Green 0;32 Light Green 1;32
Brown/Orange 0;33 Yellow 1;33
Blue 0;34 Light Blue 1;34
Purple 0;35 Light Purple 1;35
Cyan 0;36 Light Cyan 1;36
Light Gray 0;37 White 1;37
#!/bin/bash
# ANSI escape codes
GREEN='\033[0;32m'
RED='\033[0;31m'
CYAN="\[\033[0;36m\]"
GRAY="\[\033[1;30m\]"
NOC='\033[0m' # No Color
echo -e "--- [`date -R`] ($sz_descr) ($sz_IP)\t is ${RED}down${NOC}"
code,
ANSI
Exemple de us : sebas@minie:~/dades/btc$ ./btc.sh
Colorejar el prompt
Hem de modificar .bashrc
- descomentar "force_color_prompt"
vitux
Rare string operations
This method relies on enclosing a variable name in curly braces, then aplying a special operator to achieve a particular result.
Operator "#" means "delete from the left, to the first case of what follows."
$ x="This is my test string."
$ echo ${x#* }
Result :
is my test string.
Operator "##" means "delete from the left, to the last case of what follows."
$ x="This is my test string."
$ echo ${x##* }
Result :
string.
Operator "%" means "delete from the right, to the first case of what follows."
$ x="This is my test string."
$ echo ${x%* }
Result :
This is my test
Operator "%%" means "delete from the right, to the last case of what follows."
$ x="This is my test string."
$ echo ${x%%* }
Result :
This
url
Var/string length
See
string manipulation routines
myvar='Généralités'
chrlen=${#myvar} # calculate char length
oLang=$LANG oLcAll=$LC_ALL
LANG=C LC_ALL=C
bytlen=${#myvar} # calculate byte length
LANG=$oLang LC_ALL=$oLcAll
printf "%s is %d char len, but %d bytes len.\n" "${myvar}" $chrlen $bytlen
will render
Généralités is 11 char len, but 14 bytes len.
Shell functions
See man bash and find(FUNCTIONS) : "functions are executed in the context of the current shell".
In file ~/funcions/sagnd, write :
nd() { # shell function - creates and cwd's a directory
if [ ! -d $1 ]; then
mkdir $1
fi
if [ -d $1 ]; then
cd $1
fi
}
And in ~/.bash_profile include source ~/funcions/sagnd.
My Alias
The root profile is located at /root/.bashrc, the global one, at /etc/bashrc.
Private profile is ~/.bash_aliases.
echo "+++ .bash_aliases - v1.1d 20200428"
nd() { mkdir $1 && cd $1; }
web2() { clear && cd /home/nicolau/sebas/_local_tinet_files; }
# User specific aliases and functions
if [ -x /usr/bin/dircolors ]; then
alias ls='ls --color=auto'
alias grep='grep --color=auto'
alias fgrep='fgrep --color=auto'
alias egrep='egrep --color=auto'
fi
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls -als --color=auto'
alias busca='grep -i -s -m 1' // use as "busca '&&' *"
alias busca2='grep -rnwl . -e' // use as "busca2 txt"
alias cd..='cd ..'
alias cls='clear'
alias dels='sudo rm ./* -rf'
alias df='df -h | grep sd' // dont show virtual devices
alias dirs='sudo du . --max-depth=1 -m -h'
alias dirs="du -hs * | sort -rh | head -5" // show 5 largest directories
tecmint
alias findbig='find / -size +100000000c 2> /dev/null' // files bigger than 100 MB
alias fm='nautilus --browser . &' // as "start ." in guindous
alias last='last -aiFn5'
alias lok="gnome-screensaver-command -l &" // lock the computer
alias md=mkdir
alias mira="ps -ef | grep -v grep | grep"
alias mydns='cat /etc/resolv.conf' // display DNS
alias mydns='systemd-resolve --status' // ... using Network Manager
alias mydns='nmcli device show wlp3s0 | grep DNS" // get interface name from ifconfig
alias mywifi='/sbin/iwgetid' // show ESSID
alias myfs='df -h | grep sdb'
alias mygw='cat /etc/network/interfaces' // display GW
alias mygw='route -n' // ... using Network Manager
alias myhost='host myraspiodin.hopto.org'
alias myip='curl -m 2 -s icanhazip.com' // display external IP - maybe "curl -m 2 -s http://checkip.amazonaws.com" is better
alias myopsys='cat /etc/os-release'
alias mysysop='lsb_release -a'
alias mynoip2='host xarxatorrelles.ddns.net'
alias myports='sudo netstat -peanut | grep' // display open ports
alias myproxy='cat /etc/environment | grep proxy'
alias mypxe='sudo systemctl status tftpd-hpa'
alias myrestart='sudo cat /var/log/syslog | grep "Booting Linux"'
alias myubuntu='lsb_release -a'
alias myusers='sudo tail -f /var/log/auth.log'
alias mywifis='nmcli device wifi list' // show wifis around
alias pxy-users='squidclient mgr:client_list'
alias pi='sudo ping 8.8.8.8'
alias r0='ssh sebas@192.168.1.222' // ssh to piZero
alias r3='ssh sag@192.168.1.123'
alias slog='sudo tail -f /var/log/syslog'
alias testmutt='echo "my BODY" | mutt -d 5 -s "my SUBJECT" -- desti@gmail.com'
alias web='clear && cd /home/nicolau/sebas/_local_tinet_files'
alias whoiam=whoami
alias wscan='sudo iwlist wlan0 scanning | grep ESSID' // display visible wifi networks
echo "--- .bash_aliases"
After modifying .bash_aliases, run this command to get updated envir : source ~/.bash_profile
pi@raspberrypi:~ $ source ~/.bash_profile
-bash: /home/pi/.bash_profile: No such file or directory
Solucio : "exec bash" o source ~/.bashrc
After defining an alias, test it by running "alias <name>" afterward
Multiple commands in an alias for bash
alias sequence='command1 -args && command2 -args'
See command chaining
Interesting commands
Add this to your ~/.bashrc
extract () {
if [ -f $1 ] ; then
case $1 in
*.tar.bz2) tar xvjf $1 && cd $(basename "$1" .tar.bz2) ;;
*.tar.gz) tar xvzf $1 && cd $(basename "$1" .tar.gz) ;;
*.tar.xz) tar Jxvf $1 && cd $(basename "$1" .tar.xz) ;;
*.bz2) bunzip2 $1 && cd $(basename "$1" /bz2) ;;
*.rar) unrar x $1 && cd $(basename "$1" .rar) ;;
*.gz) gunzip $1 && cd $(basename "$1" .gz) ;;
*.tar) tar xvf $1 && cd $(basename "$1" .tar) ;;
*.tbz2) tar xvjf $1 && cd $(basename "$1" .tbz2) ;;
*.tgz) tar xvzf $1 && cd $(basename "$1" .tgz) ;;
*.zip) unzip $1 && cd $(basename "$1" .zip) ;;
*.Z) uncompress $1 && cd $(basename "$1" .Z) ;;
*.7z) 7z x $1 && cd $(basename "$1" .7z) ;;
*) echo "don't know how to extract '$1' ..." ;;
esac
else
echo "'$1' is not a valid file!"
fi
}
Now just type "extract filename" and you're golden.
unrar details
If you have 3 files
nom.part1.rar
nom.part2.rar
nom.part3.rar
Then the way to uncompress them all is
$ unrar x nom.part1.rar
If you dont have "unrar" in your system, just install it by "sudo apt install unrar"
SFTP and SCP - tips and problems
SCP - backup remote directory
sebas@minie:~/backups/r0/python$ scp -rp pi@r0:/home/pi/python .
$ scp -r user@your.server.example.com:/path/to/foo /home/user/Desktop/
By not including the trailing '/' at the end of foo,
you will copy the directory itself (including contents), rather than only the contents of the directory.
use -p to preserve file modification times, permissions, etc!
stackoverflow,
SCP - configuration connection problem
If you receive a "message too long" when using SFTP or SCP, as :
nicolau@mars:~/sebas/rspi/backups/r4/20220403/home/sebas/.config/systemd/user$ sftp pi@r4
pi@r4's password:
Received message too long 724249376
... then you have to clean ".bashrc" of "echo" messages, as :
# --- scp --- echo "+++ .bashrc - v1.1a 20210518"
# --- scp --- echo "--- .bashrc - v1.1a 20210518"
do FTP from shell
mate@punt-omnia:~/nodejs-projects/timer/send_page$ cat send_page.sh
#!/bin/bash
# crontab starts this job with no log specified
LogFile="/home/mate/logs/send_page.log"
# set timestamp
echo "+++ [`date -R`] +++ ("$0") +++ FTP 3 pages v3.2 +++" >> $LogFile
FileName1='/home/mate/nodejs-projects/timer/public/pagina.html'
FileName2='/home/mate/eines/rexx/scan_guifi/qq_127.html'
FileName3='/home/mate/eines/ssh/conectats.html'
HostName='files.000webhost.com'
myUser='torrelles-guifi'
myPwd='my-pwd'
# Use the ftp command with the -inv switches.
# -i turns off interactive prompting
# -n restrains FTP from attempting the auto-login feature
# -v enables verbose and progress
function ftp_it {
ftp -inv $HostName >> $LogFile 2>&1 << END_SCRIPT
quote USER $myUser
quote PASS $myPwd
binary
put $1 $2
quit
END_SCRIPT
return 0
}
ftp_it $FileName1 public_html/pagina.html
ftp_it $FileName2 public_html/qq_127.html
ftp_it $FileName3 public_html/conectats.html
echo "--- [`date -R`] +++ ("$0") +++ FTP 3 pages v3.2 ---" >> $LogFile
exit 0
Compte : queda el procés engegat (en cas d'error ?)
mate@punt-omnia:~/nodejs-projects/timer/send_page$ ps -ef | grep send_page.sh | wc
13 148 1741
scripting ftp
Error here-document :
- ending "END_SCRIPT" cant be indented
- ending "END_SCRIPT" must match the first one
- to display non-printable characters, as a SB at end, use "cat -A filename.sh"
TCPIP.shell
Get all TCP/IP data : ip, gateway, dns ...
/home/mate/bin/tcpip(.sh)
T60 : /usr/local/bin/tcpip
IF-THEN-ELSE
#!/bin/bash
echo -n "Enter a number: "
read VAR
if [[ $VAR -gt 10 ]]
then
echo "The variable is greater than 10."
else
echo "The variable is equal or less than 10."
fi
one liner
if [ -f "/usr/bin/wine" ]; then export WINEARCH=win32; fi
Conditionals with variables
#!/bin/bash
T1="foo"
T2="bar"
if [ "$T1" = "$T2" ]; then
echo expression evaluated as true
else
echo expression evaluated as false
fi
TLDP 7,
TLDP 6.
Conditional statements
IF statement
echo "Script name: $0"
NumArg=$#
echo "We have ($NumArg) arguments"
# set defaults
szStart="1652775000"
szEnd="1652775045"
outFN='/dev/null'
if [ "$NumArg" -gt 0 ]; then
szStart=$1
fi
if [ "$NumArg" -gt 1 ]; then
szEnd=$2
fi
if [ "$NumArg" -gt 2 ]; then
outFN=$3
fi
echo ">>> export using start ($szStart) and end ($szEnd) into ($outFN)."
WHILE loop and CASE statement
while :
do
clear
echo -n 'Questions? '
read x
case $x in
a)
echo a
;;
b|c)
echo b or c
;;
q)
break
;;
*)
echo Invalid command \"$x\"
;;
esac
done
url
while FOREVER loop
Explicit :
#!/bin/bash
while true
do
echo "Press [CTRL+C] to stop"
sleep 1
done
One-liner :
while :; do traceroute -n 10.139.239.102 ; sleep 1 ; done
WHILE sample
#!/bin/bash
myCOUNTER=0
while [ $myCOUNTER -lt 10 ]; do
echo "The counter is" $myCOUNTER
let myCOUNTER=$myCOUNTER+1
done
echo "End counter is" $myCOUNTER
A very complete WHILE loop sample
WHILE READ environment
Atenció : el "while read" s'executa dins un sub-shell que es genera amb el "CAT nom" amb una "pipeline"
Així, "$COUNTER" valdrà 0 després del "done" .. excepte si ho agrupem amb uns parentesi : "(" i ")"
Metode correcte aqui :
#!/bin/bash
cnt=0
cat input.data | ( while read
do
let cnt++;
echo "Counting to $cnt"
done
echo "Exit count is $cnt" )
*** while complete code ***
sebas@pi0alby:~/python/telegram $ cat prova_bash.sh
#!/bin/bash
acabat=0
rebut=0
numErr=0
maxErr=5
while [ $acabat -ne 1 ]; do
myIP=$( curl -m 2 -s icanhazip.com )
RC=$?
if [ $RC -eq 0 ]; then
let acabat=1
let rebut=1
else
let numErr=$numErr+1
if [ $numErr -eq $maxErr ]; then
let acabat=1
else
sleep 5
fi
fi
done
echo "Acabat "$acabat", rebut "$rebut", numerr "$numErr"."
if [ $rebut -eq 1 ]; then
echo "+++ MyIP {"$myIP"}."
fi
exit 0
UNTIL sample
#!/bin/bash
myCOUNTER=20
until [ $myCOUNTER -lt 10 ]; do
echo Counter $myCOUNTER
let myCOUNTER-=1
done
{repos}
BASH details
Feel free to send me your own !
Display time & date, log to file and to syslog
#!/bin/bash
fn=/home/user/logs/sebasCmd.log
date_name=`date +"%y/%m/%d"`
time_name=`date +"%H:%M"`
szId="("$time_name") +++ ("$0") +++ today is {"$date_name"} - log in file ("$fn")."
# 1st on screen
echo "$szId"
# 2nd on own log
echo "$szId" >> $fn
# 3rd on system log
logger -p user.info "$szId"
exit 0
Find large files
#!/bin/bash
find / -size +400000000c
Debug a shell
The "set -x" parameter to ksh is a useful debug feature to print commands and their arguments when executing a ksh script.
So, if you to add this on the first line, this will produce some interesting output information:
#!/bin/bash -x
exit 0
If you want to debug a shell just once, run it this way :
ksh -x script_name - it is the same as "ksh script_name" with "set -x" in the script itself
TLDP
Display directories size (1st level)
#!/bin/bash
du / -m --max-depth=1
Display largest directories at local
alias dirs='du -hs * | sort -rh | head -5'
Display Notes clients
#!/bin/bash
echo "Numero de usuarios :"
netstat -ano | grep 1352 | wc -l
echo "Las IPs de los usuarios son :"
netstat -ano | grep 1352
Veure distribució Linux
#!/bin/bash
cat /etc/issue
bash return value from a program
In bash, the return value of a program is stored in a special variable called $?.
Run this shell with an existing "/dada" and with a non-existing "/dada" directory:
#!/bin/bash
cd /dada &> /dev/null
rc=$?
echo $rc
get RC from PING or WGET
sebas@T60ubuntu:~/eines/tronc$ cat scan_tronc.sh
#!/bin/bash
export IFS='-'
# proxy Campanar :
export http_proxy=http://10.139.130.65:3128
export https_proxy=https://10.139.130.65:3128
# output file
myOF="out.txt"
# definim la funcio
function accedir {
sz_IP=$1
sz_descr=$2
echo ">>> acces a " $sz_descr", ip" $sz_IP
curl -v -k -L http://$sz_IP/ > $myOF 2>&1
rv=$?
if [ $rv -eq 0 ]; then # check the exit code
echo -e "+++ [`date -R`] ($sz_IP)\t is up"
else
echo -e "--- [`date -R`] ($sz_IP)\t is down" # echo -e == allow backslash escapes
fi
return 0
} # accedir
# definim les ips
router_campanar_tabor="172.25.66.4"
antena_campanar_tabor="172.25.66.3"
# accedim a les ips
accedir $router_campanar_tabor "Router del Campanar al Tabor"
accedir $antena_campanar_tabor "Antena del Campanar al Tabor"
exit 0
Read file into bash array
#!/bin/bash
fn=$1
echo "Lets read ("$fn") file."
cnt=0
while read curline; do
MY[$cnt]="$curline"
cnt=$(($cnt+1))
done < $fn
echo "Lines :" $cnt "Elements :" ${#MY[@]}
echo "Contingut :" ${MY[@]}
ELEM=${#MY[@]}
for (( i=0 ; i<ELEM; i++ )); do
echo "$i:" ${MY[${i}]}
done
url
#!/bin/bash
aVar1=() aVar2=() # initialize arrays
while read var1 var2
do
aVar1+=( "$var1")
aVar2+=( "$var2")
done < file.txt
# ${aVar1[@]} now contains all $var1 values,
# ${aVar2[@]} now contains all $var2 values.
url
Trapping Control-C
#!/bin/bash
# trap ctrl-c and call ctrl_c()
trap ctrl_c INT
function ctrl_c() {
echo "** Trapped CTRL-C"
exit 0
}
for i in `seq 1 5`; do
sleep 1
echo -n "."
done
File parsing
Elementary
#!/bin/bash
cat peptides.txt | while read line
do
# do something with $line here
done
With some parsing
#!/bin/bash
cat file | while read a b c; do
#process ... likely: echo "${a//search/replace} $b $c"; # etc...
done
ps details
"forest" :
$ ps -e -o pid,args --forest
ps and kill
A nice one:
#!/bin/bash
while :
do
# find parent ids
PIDS="`ps -eao bsdtime,pid,command | egrep -v egrep | egrep 'processname' | awk '{print $2}'`"
count=0
# loop through ppids and look for children
for j in ${PIDS}
do
#echo "Parent process = $j"
CPID="`ps -ef | awk '$3 == J {print $2}' J=$j`"
for i in ${CPID}
do
#echo "Child process = $i"
let count++
done
done
Perl parsing ps fwaux output
Simple :
[root@rhv6-64b reserves]# ps -ef | grep mongod | grep -v grep
root 23517 1 0 15:06 ? 00:00:04 /usr/bin/mongod --rest --config /etc/mongod.conf
[root@rhv6-64b reserves]# ps -ef | grep mongod | grep -v grep | awk '{print $1}'
root
[root@rhv6-64b reserves]# ps -ef | grep mongod | grep -v grep | awk '{print $2}'
23517
Another
$ ps -A -o pid,cmd | grep xxx | grep -v grep | head -n 1 | awk '{print $1}'
$ ps | grep '/usr/bin/mintty' | head -n 1 | awk '{print $1}'
[root@rhv6-64b reserves]# ps -A -o pid,cmd | grep mongod | grep -v grep | head -n 1 | awk '{print $1}'
23517
[root@rhv6-64b reserves]# ps -A -o pid,cmd | grep mongod | grep -v grep
23517 /usr/bin/mongod --rest --config /etc/mongod.conf
Feed ps output into kill command
Unix shell :
#!/bin/bash
tokill=$(ps -ax | grep -- "$1" | grep -v grep | awk '{print $1}')
if [[ -n $tokill ]]
then
kill $tokill
fi
pkill <process name>
or
killall <process name> if there are multiple instances and/or child processes !
Pràctiques : "T400:\\Linux\bash\"
Matem "node"
Es comode tenir :
sag@odin $ cat /home/sag/express-sendfile/atura.sh
#!/bin/bash
toKILL=$(ps -ef | grep node | grep -v grep | awk '{print $2}')
echo "want to kill ($toKILL)."
if [[ -n $toKILL ]]
then
kill $toKILL
fi
exit 0
Justify line to given length
#!/bin/bash
ten=" "
forty="$ten$ten$ten$ten"
y="very short text"
y="${y:0:40}${forty:0:$((40 - ${#y}))}"
echo "'${y}'"
mylng=${#y}
echo "char length" $mylng
Output :
'very short text '
char length 40
single quotes vs double quotes
From bash manual :
- Single Quotes
Enclosing characters in single quotes (') preserves the literal value of each character within the quotes.
A single quote may not occur between single quotes, even when preceded by a backslash.
- Double Quotes
Enclosing characters in double quotes (") preserves the literal value of all characters within the quotes,
with the exception of $, `, \, and, when history expansion is enabled, !.
The characters $ and ` retain their special meaning within double quotes.
Examples :
szIP="IPs : ext {$ipvariable}, int wlan0 {$myip}"
szSubject="$szHora - $szIP"
szBody='ODIN-002, ('$szHora'), ips ('$szIP'), host ('$HOSTNAME'), wifi ('$nom_wifi'), shell ('$0').'
If you want to write double quotes into output string, you have to "escape" them by backslash :
coding
szOut="{\"chat_id\": \"${ID}\", \"text\": \"$MENSAJE\", \"disable_notification\": true}"
produces
+++ szOut='{"chat_id": "314587090", "text": "Pi0 saluda.", "disable_notification": true}'
See shell expansions
if a=apple then
# | Expression | Result | Comments
---+-------------+-------------+--------------------------------------------------------------------
1 | "$a" | apple | variables are expanded inside ""
2 | '$a' | $a | variables are not expanded inside ''
3 | "'$a'" | 'apple' | '' has no special meaning inside ""
4 | '"$a"' | "$a" | "" is treated literally inside ''
url
Strange expansion
nicolau@mars:~/eines/bash$ cat prova.sh
#!/bin/bash
my_IP="1.2.3.4"
szOut="*** today my IP is {$my_IP} ***"
echo $szOut
echo "$szOut"
exit 0
nicolau@mars:~/eines/bash$ ./prova.sh
prova.sh today my IP is {1.2.3.4} prova.sh
*** today my IP is {1.2.3.4} ***
diferencies [[ vs [ vs ( vs ((
- ( … ) parentheses indicate a subshell
- $( … ) is a command substitution
- { … } braces are like parentheses in that they group commands, but they only influence parsing, not grouping.
- ${VAR} is a parameter expansion
- (( … )) double parentheses surround an arithmetic instruction
- [ … ] single brackets surround conditional expressions
- [[ … ]] double brackets are an alternate form of conditional expressions in ksh/bash/zsh with a few additional features
url
quotes samples
szIP="IPs : ext {$ipvariable}, int wlan0 {$myip}"
szSubject="$szHora - $szIP"
szBody='ODIN-002, ('$szHora'), ips ('$szIP'), host ('$HOSTNAME'), wifi ('$nom_wifi'), shell ('$0').'
produces
+ szIP='IPs : ext {83.38.34.204}, int wlan0 {192.168.1.123}'
+ szSubject='20:09,08/02/20 - IPs : ext {83.38.34.204}, int wlan0 {192.168.1.123}'
+ szBody='ODIN-002, (20:09,08/02/20), ips (IPs : ext {83.38.34.204}, int wlan0 {192.168.1.123}), host (odin), wifi (wlan0 ESSID:"a22"), shell (./snd_1.sh).'
This is, if we use doble quote, the $value gets expanded.
If we use single quote and want $value to be evaluated, we have to put it outside the single quotes.
own global tool
pi@R4:/usr/local/bin $ ls
0 lrwxrwxrwx 1 root root 36 Jun 5 2022 atg -> /home/pi/python/telegram/msg_a_tg.sh
basj jq command
jq tool is used to read JSON data.
linux hint
cut and sed and awk
awk - pattern scanning and processing language
sebas@minie:~$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p5 425G 220G 184G 55% /
/dev/nvme0n1p1 96M 52M 45M 54% /boot/efi
sebas@minie:~$ df -h | grep n1p5 | awk '{print $3}'
220G
cut - remove sections from each line of files
sebas@minie:~$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p5 425G 220G 184G 55% /
/dev/nvme0n1p1 96M 52M 45M 54% /boot/efi
sebas@minie:~$ df -h | grep n1p5 | cut -d ' ' -f5 #
Zach
220G
sed - stream editor for filtering and transforming text
Line endings
I has a file like this :
sebas@minie:~/dades/rexx/tinet$ cat convertir.rex
#!/usr/bin/rexx
/* trace ?r */
Pointing at
sebas@minie:~/dades/rexx/tinet$ which rexx
/usr/bin/rexx
But there was some strange problem :
sebas@minie:~/dades/rexx/tinet$ ./convertir.rex
bash: ./convertir.rex: /usr/bin/rexx^M: bad interpreter: No such file or directory
The ^M indicates the source of the problem : Windows line endings.
Solution : using SublimeText, go to "View->Line Endings" and select "Unix" and save the file.
Stack Iverflow
a bash client for an REST API
The server is coded in python
The client is quite straightforward - Gemini "write a minimal REST client in bash"
sebas@minie:~/dades/bash/rest$ cat rest_client.sh
#!/bin/bash
# Minimal REST Client in Bash
# $ ./rest_client.sh post http://192.168.1.147:5000/upload 1212
# **** ******
# Function to perform a GET request
get() {
url="$1"
echo "Use curl to perform the GET request and jq to format the JSON output"
curl -s "$url" # | jq
}
# Function to perform a POST request
post() {
url="$1"
data="$2"
# Use curl to perform the POST request with the given data
curl -s -X POST -H "Content-Type: text/plain" -d "$data" "$url" # | jq
}
# Function to perform a PUT request
put() {
url="$1"
data="$2"
echo "Use curl to perform the PUT request with the given data"
curl -s -X PUT -H "Content-Type: application/json" -d "$data" "$url" # | jq
}
# Function to perform a DELETE request
delete() {
url="$1"
# Use curl to perform the DELETE request
curl -s -X DELETE "$url" # | jq
}
# Function to display help message
help() {
echo "Usage: rest_client.sh <command> <url> [data]"
echo "Commands:"
echo " get <url> : perform a GET request"
echo " post <url> <data> : perform a POST request with data"
echo " put <url> <data> : perform a PUT request with data"
echo " delete <url> : perform a DELETE request"
echo " help : display this help message"
echo ""
echo "Example:"
echo " rest_client.sh get https://jsonplaceholder.typicode.com/todos/1"
echo " rest_client.sh post https://jsonplaceholder.typicode.com/todos '{\"title\": \"foo\", \"completed\": false}'"
}
# Check if command is provided
if [ $# -eq 0 ]; then
help
exit 1
fi
# Parse the command and arguments
command="$1"
url="$2"
data="$3"
echo ">>> command is : " $command
echo ">>> url is : " $url
echo ">>> data is : " $data
# Perform the appropriate action based on the command
case "$command" in
"get")
if [ -z "$url" ]; then
echo "Error: URL is required for GET request."
help
exit 1
fi
get "$url"
;;
"post")
if [ -z "$url" ] || [ -z "$data" ]; then
echo "Error: URL and data are required for POST request."
help
exit 1
fi
post "$url" "$data"
;;
"put")
if [ -z "$url" ] || [ -z "$data" ]; then
echo "Error: URL and data are required for PUT request."
help
exit 1
fi
put "$url" "$data"
;;
"delete")
if [ -z "$url" ]; then
echo "Error: URL is required for DELETE request."
help
exit 1
fi
delete "$url"
;;
"help")
help
;;
*)
echo "Error: Invalid command '$command'."
help
exit 1
;;
esac
exit 0
Dubtes
- com es que de vegades el comptador es
perd
i d'altres
no ?
Explicacio
- com es que se
expandeixen els "***" ?
This code
sebas@T60ubuntu:~/cron_jobs$ cat minim.sh
#!/bin/bash
szOut="*** --- curl."
echo $szOut
exit 0
produces (because of the "***")
sebas@T60ubuntu:~/cron_jobs$ ./llegir_ip_externa.sh
get_ip.sh llegir_ip_externa.sh my_crons send_ftp.sh welcome.sag --- curl.
Solution :
use double quotes when referencing echo "$var"
This gives the expected value in all the examples given.
Always quote variable references !
Why?
When a variable is unquoted, it will :
- undergo field splitting where the value is split into multiple words on whitespace (by default):
- each of these words will undergo pathname expansion, where patterns are expanded into matching files
- finally, all the arguments are passed to echo, which writes them out separated by single spaces
- get MAC : getmac script
Quina diferencia hi ha ?
if [ arg1 operator arg2 ] ; then list
if [[ arg1 operator arg2 ]] ; then list
url
2 TB - permission denied - trailing "+" in file permissions
nicolau@mars:/media/nicolau/sd2gb$ sudo cat "1" > 1.txt
bash: 1.txt: Permission denied
nicolau@mars:/media/nicolau/sd2gb$ ls
total 8
4 drwxr-xr-x 2 root root 4096 Mar 9 2021 .
4 drwxr-x---+ 4 root root 4096 Apr 16 12:50 ..
nicolau@mars:/media/nicolau$ ls
total 16
4 drwxr-x---+ 4 root root 4096 Apr 16 12:50 .
4 drwxr-xr-x 3 root root 4096 Apr 6 2020 ..
4 drwxr-xr-x 2 root root 4096 Aug 29 2021 ntfs
4 drwxr-xr-x 2 root root 4096 Mar 9 2021 sd2gb
The "+" indicates that there is an ACL (Access Control List) entry associated with the file.
stack overflow,
nicolau@mars:/media/nicolau/sd2gb$ getfacl -a ..
# file: ..
# owner: root
# group: root
user::rwx
user:nicolau:r-x
group::---
mask::r-x
other::---
Solucio
nicolau@mars:/media/nicolau$ sudo chmod 777 sd2gb/
trailing "t" in file permissions
nicolau@mars:/$ ls -al
...
4 drwxrwxrwt 16 root root 4096 Apr 18 19:00 tmp
What does "t" mean ?
The d letter means it's a directory (a folder if you prefer that name).
The t letter means that file is 'sticky'.
Only the owner and root can delete a sticky file.
unix.stackexchange