Process Input Line by Line
1#!/bin/bash
2input="path/to/file"
3while IFS= read -r line
4do
5 # process each read-in line
6 echo "$line"
7done < "$input" # NORICE HERE
Pay attention to the <
part following done
on the last line. It redirects the $input
file to stdin
of read
process, making read
reads the file line by line and then do the processing in the loop body. As for the preceding IFS=
, there is a section
explains it in this post.
References: Linux/UNIX: Bash Read a File Line By Line 1 on nixCraft .
shift
Built-in
1if [ $# != 0 ]; then
2 while true; do
3 case "$1" in
4 --first-option)
5 $ARG="$1"
6 shift
7 ;;
8 --second-option)
9 $NEXT_ARG="$1"
10 shift
11 ;;
12 # --more-option)
13 # ...
14 esac
15 done
16fi
shift NUM
shifts the positional parameters
($1
, $2
etc) to the left by NUM
. If unset, NUM
is 1. Check the Bash manual
for more details.
Parameter Substitution
${var#Pattern}
,${var##Pattern}
From the front,#
matches the shortest part,##
matches the longest part${var%Pattern}
,${var%%Pattern}
From the back,%
matches the shortest part,%%
matches the longest part
Conditional Expression
For strings:
[ STRING == STRING ]
True if two strings are equal. **"=" may be used instead of “==” for strict POSIX compliance.**[ -z STRING ]
True if the string is empty[ -n STRING ]
or[ STRING ]
True if the string is non-empty For files:[ -e FILE ]
Exists.[ -f FILE ]
Regular file.
:
Built-in
It’s a Bourne Shell’s built-in and works as true
(with some minor differences stated here
). Example:
1:> file # simply create a file and nothing else
References:
${BASH_SOURCE[0]}
Variable
BASH_SOURCE
is a built-in array variable that stores the script path of functions on the call stack, and ${BASH_SOURCE[0]}
is the script path of the top of the stack, i.e. the script of the current executing code.
It’s similar to $0
but with the following difference. For a script called foo
:
1#!/bin/bash
2echo "[$0] v.s. [$BASH_SOURCE]"
Run the following commands:
1$ bash ./foo
2[./foo] v.s. [./foo]
3$ ./foo
4[./foo] v.s. [./foo]
5$ . ./foo
6[bash] v.s. [./foo]
In other words, it works in both sourcing and executing scenarios. Last but not least, it’s a Bash variable and not POSIX-compliant.
References:
Sourcing or Executing a script?
As this answer states:
Sourcing a script will run the commands in the current shell process.
Executing a script will run the commands in a new shell process.
1# Executing
2./my_script
3my_script
4# Sourcing
5source my_script
6. my_script
IFS
Variable
$IFS
, which stands for Internal Field Separator, is a special shell variable that specifies delimiters to split strings into words in loops2:
1IFS=","
2INPUT="a,b,c,d"
3for field in ${INPUT}
4do
5 # ...
6done
Or with the read
built-in command:
1cat > file << "EOF"
2big_machine|202.54.1.1|/home/alice|Alice
3small_machine|202.54.1.2|/home/bob|Bob
4EOF
5
6IFS='|'
7while read -r hostname ip homedir username
8do
9 # ...
10done < file
More information on:
- this example of RIP Tutorial
- this wiki page of Linux Shell Scripting Tutorial
set
Built-in
As mentioned in the Bash Manual :
This built-in is so complicated that it deserves its own section.
set
is a versatile Bash built-in command having multiple functions. Therefore we only list those that are encountered before.
set -/+<opts>
turns on/off shell options-e
make Bash stop executing the script if a command in the script returns an error.
set -- [args]
sets the positional parameters toargs
. More example here .
Copy All (including hidden) Files in a Directory
As this answer suggests:
1mkdir /home/<new_user>
2cp -r /etc/skel/. /home/<new_user>
3# ^ the dot here makes things work
Change Password in Script
Use chpasswd
. In its manual:
NAME
chpasswd
- update passwords in batch mode……
DESCRIPTION The
chpasswd
command reads a list of user name and password pairs from standard input and uses this information to update a group of existing users. Each line is of the format:1user_name:password
Example of using it:
1echo 'user_name:password' | chpasswd
List Files with Full Path
As recommended by this post :
1ls -d $PWD/*
And another option by this answer :
1find -type f -maxdepth 1 "$(PWD)"
I personally prefer the first one, which I think is simpler and more straightforward.
“find
, and then -exec
”
There is a option -exec
of the find
command, which specifies the action to take for those files that are found out by the command. Examples:
1find ./ -type f -exec chmod 644 {} \;
2
3find . -exec /bin/rm {} \; # useful when you can't do `rm *` because there are too many files and Bash can't manage their names
The semicolon ;
following -exec COMMAND
is to separate the command body with other arguments of find
3. Note that Bash takes ;
as a list operator
, so in order to keep it as a literal and pass it to find
, we need to escape it to remove its special meaning to Bash (example provided by this answer
):
1find . -exec echo {} \;
2find . -exec echo {} ';'
3find . -exec echo {} ";"
4find . -exec echo {} \+
5find . -exec echo {} +
Check this post for more examples.
Bash Print with Color
1function __print() {
2 #color
3 local COLOR_NONE="\e[0m"
4 local COLOR_RED="\e[0;31m"
5 local COLOR_GREEN="\e[0;32m"
6 local COLOR_YELLOW="\e[0;33m"
7
8 #debug level
9 local INFO=1
10 local ERR=2
11 local STEP=3
12
13 color=$COLOR_NONE
14 if [ $1 == $INFO ];then
15 color=$COLOR_GREEN
16 elif [ $1 == $ERR ];then
17 color=$COLOR_RED
18 elif [ $1 == $STEP ];then
19 color=$COLOR_YELLOW
20 fi
21
22 echo -e $color"$2"$COLOR_NONE
23}
-
In the article they mention some bash-scripting concepts like process substitution (Linux Journal ), here strings and here documents . Check out those links for detailed information. ↩︎
-
More precisely, in all scenarios involving word splitting . Check this for more examples. ↩︎
-
Character
+
could share the similar function with;
. Seefind
’s manual to see their difference. ↩︎