読者です 読者をやめる 読者になる 読者になる

pikesaku’s blog

個人的なプログラム勉強メモです。記載内容について一切の責任は持ちません。

シェルを学ぶ

参考URLのシェルスクリプトがかっこいいので勉強
シェルのテクニック満載で、これでもかっ!ってくらいあった気がする。大変勉強になりました。

要チェックキーワード

FIXMEは修正を要する部分

変数を${VAR}と$VARで定義する場合の違い

参照時に変数名部分を明示的にする
変数を使用する | UNIX & Linux コマンド・シェルスクリプト リファレンス
 

# cat ./a.sh
#!/bin/ksh

A="1"
B="2"
echo $AB
echo ${A}B
echo ${A}${B}
# sh ./a.sh

1B
12



環境変数 LC_ALL

ロケールに関する環境変数の一括上書き

# echo $LANG
ja_JP.utf8
# ls /tmp/noexist                                                                            
ls: /tmp/noexist にアクセスできません: そのようなファイルやディレクトリはありません
# export LC_ALL=C
# echo $LANG
ja_JP.utf8
# ls /tmp/noexist
ls: cannot access /tmp/noexist: No such file or directory
# 



シェルで未定義変数を参照した時にエラー終了させる

# cat /tmp/a.sh 
#!/bin/bash

set -u
echo $not_defined
echo test
# sh /tmp/a.sh 
/tmp/a.sh: 行 4: not_defined: 未割り当ての変数です
# 

 

setコマンドはshellにオプションを設定するコマンド

${VAR+set}の意味

変数の定義状況の確認で使う
この"set"は他の文字列でもOK。変数定義状況確認を示す為、setを使っているだけ

以下URLによると
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
 
${VAR+hoge}の場合

VAR変数定義あり かつ NULLでない場合
→${VAR+hoge}はhoge

VAR変数定義あり かつ NULLである場合
→${VAR+hoge}はhoge

VAR変数定義なし
→${VAR+hoge}はNULL

では確認

# cat ./a.sh 
#!/bin/bash

A="test"
echo ${A+hoge}
A=""
echo ${A+hoge}
echo ${B+hoge}
# sh ./a.sh
hoge
hoge

 


ちなみに、
${VAR:+word}の場合
VARにNULL以外の値がセットされているかの判定に使える。

# cat ./a.sh 
#!/bin/bash

A="test"
echo ${A:+hoge}
A=""
echo ${A:+hoge}
echo ${B:+hoge}
# sh ./a.sh
hoge



expressionを囲う角括弧が1個の場合と2個の場合の違いは

※[]と[[]]の違い

man bashでは、、、
expression の説明に以下記述があり。

Word splitting and pathname expansion are not performed on the words between the and ; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed.

すごく詳しい解説があった!
https://fumiyas.github.io/2013/12/15/test.sh-advent-calendar.html
 

①ワード分割とパス名展開がされない

例)

# [ -f a ] && echo Exist
Exist
# [ -f a* ] && echo Exist
-bash: [: too many arguments
# [[ -f a ]] && echo Exist
Exist
# [[ -f a* ]] && echo Exist
# [[ -f a? ]] && echo Exist
# A="hoge hoge"
# [ -f $A ] && echo Exist
-bash: [: hoge: binary operator expected
# [ -f "$A" ] && echo Exist
# [[ -f $A ]] && echo Exist
# 

 


②数値の比較演算子では左右の値が算術式展開される

例)

# [ 1+2 -eq 3 ] && echo "Ugoku"
-bash: [: 1+2: integer expression expected
# [[ 1+2 -eq 2+1 ]] && echo "Ugoku"
Ugoku
# [[ '1 + 2' -eq '2 + 1' ]] && echo "Ugoku"
Ugoku

 

ポイントは、式に空白を含める場合は、クオートする必要がある点

③文字列の比較演算子 == で右辺がクオートされてない場合、完全一致ではなくパターンマッチになる点

例)

# [ 'test' == tes* ] && echo Match
# [[ 'test' == tes* ]] && echo Match
Match
# [[ 'test' == "tes*" ]] && echo Match
# 

 


④[]にはない文字列の比較演算子を使える。

例)
正規表現が可能。こりゃ便利

# [ "test" =~ ^[a-z]{4}$ ] && echo Match
-bash: [: =~: binary operator expected
# [[ "test" =~ ^[a-z]{4}$ ]] && echo Match
Match

 <や>で文字列順番の比較が可能

# [[ "abc" < "def" ]] && echo Match
Match
# [[ "abc" > "def" ]] && echo Match
# 

 


ちなみに[]でやると、<>はリダイレクトが動いてしまい、こんな動きになる。

# [ "abc" > "def" ] && echo "Match"
Match
# ls ./def 
./def
# [ "abc" < "def" ] && echo "Match"
Match
# ls ./abc ./def                                                                                                                                                        
ls: cannot access ./abc: No such file or directory
./def
# rm -f ./def 
# [ "abc" < "def" ] && echo "Match"
-bash: def: No such file or directory
# 

 


⑤条件式の論理演算子(and・or)の記述が異なる。は-a,-o、[]は&&、||

[]の場合

# [ 1 -eq 1 -a 2 -eq 2 ] && echo Match
Match
# [ 1 -eq 1 -o 2 -eq 2 ] && echo Match
Match
# [ 1 -eq 1 && 2 -eq 2 ] && echo Match
-bash: [: missing `]'
# [ 1 -eq 1 || 2 -eq 2 ] && echo Match
-bash: [: missing `]'
-bash: 2: command not found

 


[[]]の場合

# [[ 1 -eq 1 && 2 -eq 2 ]] && echo Match
Match
# [[ 1 -eq 1 || 2 -eq 2 ]] && echo Match
Match
# [[ 1 -eq 1 -a 2 -eq 2 ]] && echo Match
-bash: syntax error in conditional expression
-bash: syntax error near `-a'
# [[ 1 -eq 1 -o 2 -eq 2 ]] && echo Match
-bash: syntax error in conditional expression
-bash: syntax error near `-o'
# 

結論: これからは、上記を踏まえ角括弧2個を使う!正規表現が使えるのがいい!


function pdieのexit ${2-1}の意味について

function pdie {
  perr "$1"
  exit ${2-1}
}

 


http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
${VAR-hoge}は上記表では以下の様に展開される。

VAR変数定義あり かつ NULLでない場合
→${VAR-hoge}はVARの値

VAR変数定義あり かつ NULLである場合
→${VAR-hoge}はNULL

VAR変数定義なし
→${VAR-hoge}はhoge

終了ステータスコードをpdie呼び出し時の第二引数で指定できる

簡易動作確認コード

# cat ./a.sh 
#!/bin/ksh

function hoge {
  echo ${2-1}
}

hoge
hoge ""
hoge "" ""
hoge "a" ""
hoge "a" "b"
# sh ./a.sh 
1
1


b



[[ -t 0 ]]の意味について

ファイルデスクリプタ0(標準入力)があるかを確認
以下をターミナル・cronで、それぞれ実行すると結果は異なる。
[[ -t 0 ]]; echo $?
ターミナル経由の場合は0(成立)
cron経由の場合は1(非成立)

以下でやっていることは、、、

function run {
  pinfo "Run command: $*" 1>&2
  if [[ -n ${NO_RUN+set} ]]; then
    [[ -t 0 ]] || cat >/dev/null
  else
    "$@"
  fi
}

 


パイプとファイルディスクリプタの仕組み理解できていないが動作確認した結果では、、、
・run関数がパイプ経由で呼び出された場合、[[ -t 0 ]]は成立しない
・run関数がパイプ経由で呼び出されていない場合、[[ -t 0 ]]は成立する

このあたりの動作は以下URLの情報が関係あるのかな。。。
http://blog.livedoor.jp/cielo_cielo/archives/65111675.html

ひとまず上記より以下の事が判明
NO_RUN環境変数の定義がある場合
・パイプで呼び出された場合、標準入力は/dev/null行き
・パイプで呼び出されてない場合、何も実行されない
→要は何も実行されないし、スクリプト実行したコンソールには何も出力されない。

NO_RUN環境変数の定義がない場合
"$@"が実行される。"$@"は関数の引数全て。このスクリプトではrun関数にはコマンドが引数に指定されている。

・パイプで呼び出された場合、標準入力はrun関数の引数に指定されたteeコマンドの標準入力になる。
・パイプで呼び出されてない場合、引数で指定されたコマンドが実行される。

run関数で色々なコマンドを実行する。コマンド自体に引数を指定する場合、パイプ経由で呼び出す。


[[ ${1-} = @(|0) ]]の意味について

以下のコードがあり

function fml_true_p {
  [[ ${1-} = @(|0) ]] && return 1
  return 0
}

 


まず、"="について。"=="じゃないの?
man bashを見ると、testコマンドの文字列比較では、==だけでなく=も使えて、POSIX準拠するには=の方がよいと記載があり。

string1 == string2
string1 = string2
True if the strings are equal. = should be used with the test command for POSIX conformance.<<

次に${1-}について

${1-}は、以下の様に展開される。
①第一引数がある場合、第一引数の値
②第一引数があるが空文字の場合、空文字
③第一引数がない場合、②と同じ(=空文字)

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
${1-}は上記URLのwordが省略された場合

最後に@(|0)について

@(|0)は、man bashによるとシェルのワイルドカードマッチの記述で、man bashGoogle翻訳によると

 いくつかの拡張パターンマッチング演算子が認識されます。以下の説明では、パターンリストは、|で区切られた1つ以上のパターンのリストである。複合パターンは、以下のサブパターンのうちの1つ以上を使用して形成することができる。

 ?(パターンリスト)
 与えられたパターンの0回または1回のオカレンスと一致します。
 *(パターンリスト)
 与えられたパターンの0回以上の出現にマッチします。
 +(パターンリスト)
 与えられたパターンの1つ以上の出現と一致します。
 @(パターンリスト)
 指定されたパターンの1つに一致します。

少し分かりづらい。動作確認したところ以下の通り

①?(PTRN_LIST)の場合

# [[ 1 = ?(1|2|3) ]] && echo  yes
yes
# [[ 0 = ?(1|2|3) ]] && echo  yes
# [[ 10 = ?(1|2|3) ]] && echo  yes
# [[ 13 = ?(1|2|3) ]] && echo  yes
# 

 


②*(PTRN_LIST)の場合

# [[ 1 = *(1|2|3) ]] && echo  yes
yes
# [[ 0 = *(1|2|3) ]] && echo  yes
# [[ 10 = *(1|2|3) ]] && echo  yes
# [[ 13 = *(1|2|3) ]] && echo  yes
yes
# 

 


③+(PTRN_LIST)の場合

# [[ 1 = +(1|2|3) ]] && echo  yes
yes
# [[ 0 = +(1|2|3) ]] && echo  yes
# [[ 10 = +(1|2|3) ]] && echo  yes
# [[ 13 = +(1|2|3) ]] && echo  yes
yes
# 

 


④@(PTRN_LIST)の場合

# [[ 1 = @(1|2|3) ]] && echo  yes
yes
# [[ 0 = @(1|2|3) ]] && echo  yes
# [[ 10 = @(1|2|3) ]] && echo  yes
# [[ 13 = @(1|2|3) ]] && echo  yes
# 

 


上記よりパターン定義した文字列(上記の場合1,2,3)が、何回展開されるかの違い。?は0or1回。@(1|2|3)を展開しても、1文字にしかならない。その為、10や13はマッチしない。*は0回以上、+は1回以上だから13がマッチする。@は1回だけなので13はマッチしない。

補足として以下動作を見るとより分かりやすい。

# [[ 0 = ?(1|2|3)0 ]] && echo  yes
yes
# [[ 0 = *(1|2|3)0 ]] && echo  yes
yes
# [[ 0 = +(1|2|3)0 ]] && echo  yes
# [[ 0 = @(1|2|3)0 ]] && echo  yes
# 

 


まあ、manに書いてある通り。

fml_true_p関数の動きは、以下の通り

第一引数がない時 return 1
第一引数が空文字の時 return 1
第一引数が上記以外の時 return 0

 

${0##*/}の意味について

以下のコードがあり

tmp_dir=$(mktemp -d /tmp/${0##*/}.XXXXXXXX) || pdie "Cannot create temporary directory"

 


これは、ここにも記載した内容
pikesaku.hatenablog.com

要は変数の値に対しパターンを指定して、部分的に切り出す操作。
${0##*/}は、${0}がスクリプト名に展開されるので、スクリプト名から最長一致で/が取り除かれるので、ファイル名だけに展開される。

例)

# cat ./a.sh
#!/bin/bash

echo '${0}     -> ' ${0}
echo '${0##*/} -> ' ${0##*/} 
# sh ./a.sh
${0}     ->  ./a.sh
${0##*/} ->  a.sh
# 

 

mktempコマンド

テンポラリファイルのファイル名を生成するコマンド。

# mktemp /tmp/a.XXXXX
/tmp/a.591zh

引数はテンプレート。X部分はランダム文字列が生成されて割り当てられる。

trapコマンド

以下URLが分かりやすい
シグナルと trap コマンド | UNIX & Linux コマンド・シェルスクリプト リファレンス

シグナルハンドラ。シェルにもあったんだ。。。。

trap 'コマンド' シグナルリスト
例) # trap 'echo finish;exit' INT HUP

${MAILMAN_USER-mailman}の意味。

${VAR-word}。環境変数があったら、そっちを使う。なければデフォルトのmailman使う。

よくよく考えると、このスクリプトはコードを変更しなくても、色々な環境で使えるように考えてくれているんだな。
きめ細かい心遣いに感謝。

shiftコマンド

引数を1つづらす

# cat /tmp/a.sh 
#!/bin/bash

for i in $(seq 1 3)
do
  echo $1; shift
done
# sh /tmp/a.sh a b
a
b

 

mm_url_host="${1-}"; ${1+shift}の意味

引数があったら、その値を変数に入れて、更にshiftを実行する。

# cat /tmp/a.sh                                                                              
#!/bin/bash

a=$1; shift
b=$1; shift
c=${1-};
${1+shift}
d=$1
echo "a is $a"
echo "b is $b"
echo "c is $c"
echo "d is $d"
# sh /tmp/a.sh 1 2 3 4 5                                                                     
a is 1
b is 2
c is 3
d is 4

 

typesetコマンド

色々機能があるようだけど、このスクリプトでは以下オプションを使っている。

・-lオプション
変数の値を小文字にする

# cat /tmp/a.sh 
#!/bin/bash

a="A"
echo $a
typeset -l a
a="A"
echo $a
# sh /tmp/a.sh 
A
a

 


・-uオプション
変数の値を大文字にする

・-Aオプション
associative array(連想配列)を宣言する。

# cat /tmp/a.sh 
#!/bin/bash

typeset -A aa
aa["id"]="bb"
echo ${aa["id"]}
# sh /tmp/a.sh 
bb

 


※展開時は、{}で囲う必要あり。囲わないと展開されない。(RHEL7 bash 4.2.46)

read -rについて

・-rオプションでバックスラッシュを、エスケープ文字として扱わなくなる。

# cat /tmp/a.sh 
#!/bin/bash

echo "# no defined -r"
(echo "a";echo 'b\';echo "c") \
| while read a
do
  echo $a
done

echo "# defined -r"
(echo "a";echo 'b\';echo "c") \
| while read -r a
do
  echo $a
done
# sh /tmp/a.sh 
# no defined -r
a
bc # b\の\が評価された為、echoコマンドが付与する改行コードが無効化され1行になる
# defined -r
a
b\
c

 

以下のsed正規表現の意味

sed \
  -n \
  -e 's/^\$\([A-Za-z][A-Za-z_]*\)[ 	]*=[ 	]*\(.*\);[ 	]*$/\1 \2/p' \
  -e 's/^[ 	]*&*DEFINE_FIELD_FORCED(.\([^"'"'"']*\).[ 	]*,[ 	]*\([^)]*\).*$/\1 \2/p' \
  config.ph \

 


・-nは出力抑制
・-eはスクリプト実行

まず以下の正規表現について

-e 's/^\$\([A-Za-z][A-Za-z_]*\)[      ]*=[    ]*\(.*\);[      ]*$/\1 \2/p'

AAA = "aaa";
上記だと、AAAと"aaa"を出力する。※[]はページ表示されうとスペース1文字に見えるが、ソースコードではスペースとタブ。

次に以下の正規表現について

-e 's/^[      ]*&*DEFINE_FIELD_FORCED(.\([^"'"'"']*\).[       ]*,[    ]*\([^)]*\).*$/\1 \2/p'

 


以下のパターン記述の意味は?

[^"'"'"']

 


上記はダブルクオート・シングルクオートではない文字がマッチする。
sedスクリプトがシングルクオートを囲まれている場合に、スクリプト内でシングルクオートを使うには、上記の記述が必要。

^の後の文字列は以下に分解されて解釈されている。

"

→ダブルクオート1文字の意味

'"'"'

 


→シングルクオート1文字の意味。シェルはシングルクオートで括られた上記をシングルクオート1つに展開する。

例)

# echo ''"'"''                                                                                                                                                          
'
# 

 


以下URLが根拠かと思われる。こんな展開ができるとは。。。

bash - How to escape single-quotes within single-quoted strings? - Stack Overflow
http://sed.sourceforge.net/grabbag/tutorials/sedfaq.txt
quoting - How to echo `single quote` when using single quote to wrap special characters in shell? - Unix & Linux Stack Exchange

シングルクオート内でシングルクオートをリテラルとして使いたい時のテクニック

例)

# echo '"'"'
> ^C →Ctrl+Cで中断。これだと、2個目のダブルクオートが閉じてない為、コマンドが実行されない。
# echo '\''                                                                                                                                    
> ^C →Ctrl+Cで中断。これだと、3個目のシングルクオートが閉じていない為、コマンド実行がされない。シングルクオート内ではバックスラッシュもリテラル扱いになる。
# echo ''"'"''
'

 


で、結局この正規表現

  -e 's/^[ 	]*&*DEFINE_FIELD_FORCED(.\([^"'"'"']*\).[ 	]*,[ 	]*\([^)]*\).*$/\1 \2/p' \
&DEFINE_FIELD_FORCED("reply-to", "$From_address, $MAIL_LIST");

 


上記だと、reply-toと"$From_address, $MAIL_LIST"が出力される。
例)

# echo  '&DEFINE_FIELD_FORCED("reply-to", "$From_address, $MAIL_LIST");' | sed -n -e 's/^[ ]*&*DEFINE_FIELD_FORCED(.\([^"'"'"']*\).[ ]*,[ ]*\([^)]*\).*$/\1/p'
reply-to
# echo  '&DEFINE_FIELD_FORCED("reply-to", "$From_address, $MAIL_LIST");' | sed -n -e 's/^[ ]*&*DEFINE_FIELD_FORCED(.\([^"'"'"']*\).[ ]*,[ ]*\([^)]*\).*$/\2/p'
"$From_address, $MAIL_LIST"
# 

 

cf_value="${cf_value//\\@/@}"の意味

man kshGoogle翻訳では

パラメータを展開し、パターンの最長一致を指定された文字列に置き換えます。文字列中の\ nのそれぞれの出現は、n番目のサブパターンと一致するパラメータの部分に置き換えられます。最初の形式では、patternの最初のオカレンスのみが置き換えられます。 2番目の形式では、patternの各一致が指定された文字列に置き換えられます。 3番目の形式はパターンマッチを文字列の先頭に限定し、4番目の形式はパターンマッチを文字列の末尾に限定します。 stringがヌルの場合、パターンは削除され、文字列の/ inは省略されます。パラメータが@、*、添字@または*を持つ配列変数の場合、各要素に代入演算が交互に適用されます。この場合、単語の文字列部分は各要素について再評価されます。

全体的に分からないうえに、'/'と'//'の違いが分からない。。。。
変数の値に対しパターンマッチした部分を置き換えるよう。
動作確認した結果は以下の通り。

# A='ababab'
# B=${A//ab/AB}
# echo $B
ABABAB
# A='ababab'
# B=${A/ab/AB}
# echo $B
ABabab

 


'/'の場合は最初にマッチした部分だけ置換、'//'は全部!
やっている事は、\@を@へ置き換え。

fml_cf[$cf_name]="${cf_value/\$DOMAINNAME/${fml_cf[DOMAINNAME]-}}"の意味

"${fml_cf[DOMAINNAME]-}"の-は、${A-}と同じ。
fml_cf[DOMAINNAME]が定義済みであれば、その値、未定義or空であれば空文字に置換される。

mm_postid=$(cat seq 2>/dev/null) && let mm_postid++の意味

letは算術展開する。動作は以下の通り。

# let "A=2*3"
# echo $A
6
# let "A++"
# echo $A
7
# let "A+1"
# echo $A
7
# let "A=A+1"
# echo $A
8
# 

 

mm_subject_post_id_fmt="%0${fml_cf[SUBJECT_FORM_LONG_ID]-5}d"の意味

これは、以下の動き

"${fml_cf[SUBJECT_FORM_LONG_ID]"が空文字でなければ、その値。空文字なら5

mm_subject_prefix="${fml_cf[SUBJECT_TAG_TYPE]/ /${fml_cf[BRACKET]} $mm_subject_post_id_fmt}"の意味

これは、以下の動き

# A='(:)'
# B="${A/ /test hoge}"
# echo $B
(:)
# A='(:) '
# B="${A/ /test hoge}"
# echo $B
(:)test hoge
# 

 

以下sedの意味

sed -n \
      -e '1 {h; $ !d}' \
      -e '$ {x; s/\n / /g; p}' \
      -e '/^ / {H; d}' \
      -e '/^ /! {x; s/\n / /g; p}' \

 


参考
Unix Sed Tutorial : 7 Examples for Sed Hold and Pattern Buffer Operations
 

スクリプトの処理内容は以下の通り

1個目のスクリプトの処理
-e '1 {h; $ !d}' \

 


1行目であれば以下の処理をする。
・パターンスペースをホールドスペースにコピー
・最終行でなければパターンスペースを削除

2個目のスクリプトの処理
-e '$ {x; s/\n / /g; p}' \

 


最終行であれば以下の処理をする。
・パターンスペースとホールドスペースを入れ替える。
・改行+スペースをスペースに置換(?)
・パターンスペースを出力

3個目のスクリプトの処理
-e '/^ / {H; d}' \

 


スペースで始まる行の場合、以下の処理をする。
・パターンスペースをホールドスペースに追記
・パターンスペースを削除

4個目のスクリプトの処理
-e '/^ /! {x; s/\n / /g; p}' \

 


スペースで始まらない行の場合、以下の処理をする。
・パターンスペースとホールドスペースを入れ替える。
・改行+スペースをスペースに置換(?)
・パターンスペースを出力

上記の動作を踏まえ、以下の実行結果をトレースしてみる。

# cat ./a
hoge1: inu
hoge2: neko
 kuma
hoge3: saru
# sed -n -e '1 {h; $ !d}' -e '$ {x; s/\n / /g; p}' -e '/^ / {H; d}' -e '/^ /! {x; s/\n / /g; p}' ./d
hoge1: inu
hoge2: neko kuma
hoge3: saru# 

 

①1行目処理

1行目データがパターンスペースに読み込まれる

パターンスペース hoge1: inu
ホールドスペース -

1個目のスクリプト(-e '1 {h; $ !d}')の処理
パターンスペースの内容がホールドスペースにコピーされる。

パターンスペース hoge1: inu
ホールドスペース hoge1: inu

パターンスペースの内容が削除される。

パターンスペース -
ホールドスペース hoge1: inu

2個目のスクリプト(-e '$ {x; s/\n / /g; p}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース -
ホールドスペース hoge1: inu

3個目のスクリプト(-e '/^ / {H; d}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース -
ホールドスペース hoge1: inu

4個目のスクリプト(-e '/^ /! {x; s/\n / /g; p}')の処理
パターンスペースとホールドスペースの内容を置き換える。

パターンスペース hoge1: inu
ホールドスペース -

パターンスペース(hoge1: inu)の内容を出力する。
 

②2行目処理

2行目データがパターンスペースに読み込まれる

パターンスペース hoge2: neko
ホールドスペース -

1個目のスクリプト(-e '1 {h; $ !d}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース hoge2: neko
ホールドスペース -

2個目のスクリプト(-e '$ {x; s/\n / /g; p}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース hoge2: neko
ホールドスペース -

3個目のスクリプト(-e '/^ / {H; d}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース hoge2: neko
ホールドスペース -

4個目のスクリプト(-e '/^ /! {x; s/\n / /g; p}')の処理
パターンスペースとホールドスペースの内容を置き換える。

パターンスペース -
ホールドスペース hoge2: neko

パターンスペース(空)の内容を出力する。
 

③3行目処理

3行目データがパターンスペースに読み込まれる

パターンスペース kuma
ホールドスペース hoge2: neko

1個目のスクリプト(-e '1 {h; $ !d}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース kuma
ホールドスペース hoge2: neko

2個目のスクリプト(-e '$ {x; s/\n / /g; p}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース kuma
ホールドスペース hoge2: neko

3個目のスクリプト(-e '/^ / {H; d}')の処理
パターンスペースの内容をホールドスペースに追記

パターンスペース kuma
ホールドスペース hoge2: neko
kuma

パターンスペースを削除

パターンスペース -
ホールドスペース hoge2: neko
kuma

4個目のスクリプト(-e '/^ /! {x; s/\n / /g; p}')の処理
パターンスペースとホールドスペースを入れ替える

パターンスペース hoge2: neko
kuma
ホールドスペース -

改行+スペースをスペースに置換

パターンスペース hoge2: neko kuma
ホールドスペース -

パターンスペース(hoge2: neko kuma)の内容を出力する。

パターンスペース hoge2: neko kuma
ホールドスペース -

 

④4行目(最終行)処理

4行目データがパターンスペースに読み込まれる

パターンスペース hoge3: saru
ホールドスペース -

1個目のスクリプト(-e '1 {h; $ !d}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース hoge3: saru
ホールドスペース -

2個目のスクリプト(-e '$ {x; s/\n / /g; p}')の処理
パターンスペースとホールドスペースを入れ替える

パターンスペース -
ホールドスペース hoge3: saru

パターンスペース(空)の内容を出力する。

3個目のスクリプト(-e '/^ / {H; d}')の処理
アドレス条件が合致しない為、何も処理されない。

パターンスペース -
ホールドスペース hoge3: saru

4個目のスクリプト(-e '/^ /! {x; s/\n / /g; p}')の処理
パターンスペースとホールドスペースを入れ替える

パターンスペース hoge3: saru
ホールドスペース -

パターンスペース(hoge3: saru)の内容を出力する。

この処理の意味

aliasファイルの同一エントリに改行が含まれる場合を想定していると思われる。

# cat ./a
hoge1: inu
hoge2: neko
 kuma
hoge3: saru
# postalias ./a
# db_dump -p ./a.db                                                                                                                                                     
~省略~
 hoge1\00
 inu\00
 hoge3\00
 saru\00
 hoge2\00
 neko kuma\00
DATA=END
# 

 


※hoge2はnekoとkumaに変換される。

sort -ufについて

-u(unique)オプション

sort | uniqと同じ。

# cat ./a
a
b
c
a
b
c
# sort -u ./a
a
b
c
# 

 

-fオプション

大文字・小文字を問わず。

# cat ./a
a
B
c
A
b
C
# sort -uf ./a
a
B
c
# 

 

|tee >(sed 's/^/INFO: Mailman withlist: /' 1>&2)の意味

動きはこんな感じ

# cat ./a
a
b
c
# cat ./a | tee >(sed 's/^/INFO: Mailman withlist: /' 1>&2) | cat
a
b
c
INFO: Mailman withlist: a
INFO: Mailman withlist: b
INFO: Mailman withlist: c
# cat ./a | tee >(sed 's/^/INFO: Mailman withlist: /' 1>&2) | cat > /dev/null
INFO: Mailman withlist: a
INFO: Mailman withlist: b
INFO: Mailman withlist: c
# 

  


manのteeには、この使い方は記載なし。infoにあった。
どうやら"プロセス置換"という機能のよう。

# info coreutils 'tee invocation'

 


bashのプロセス置換機能を活用して、シェル作業やスクリプト書きを効率化する - 双六工場日誌
Bash のプロセス置換が便利な件 - 理系学生日記

こりゃまた便利な機能だ!!!!
以下のように使える。
①コマンド出力をファイルとして見立ててくれる。

# cat ./a
a
b
c
# cat ./b
a
b
d
# diff -u <(cat ./a) <(cat ./b)
--- /dev/fd/63  2016-12-04 21:30:23.743192617 +0900
+++ /dev/fd/62  2016-12-04 21:30:23.743192617 +0900
@@ -1,3 +1,3 @@
 a
 b
-c
+d
#

 


ファイルの出力結果をファイルに変換してくれている。


②コマンドによるファイルへの入力をコマンドで受ける事ができる。
 ※コマンドをファイルをように見立ててくれる。

# echo "test" | tee ./hoge
test
# cat ./hoge 
test
# echo "test" | tee >(sed 's/test/hoge/') | cat
test
hoge
#

 


teeは引数にファイルを取り、出力をファイルと標準出力にだす。
このファイル部分を">(コマンド)"で置換している。

testの-sオプション

ファイルが存在してサイズが0より大きい場合にマッチ

lsの-fと-Fオプション

-fオプション

manでは以下説明

do not sort, enable -aU, disable -ls --color

以下を有効にして
"-a" .付きファイルを出力
"-U" ソートせずディレクトリオーダー順に出す。

以下を無効にした時と同じ
"-l" ロングフォーマット出力
"-s" サイズを出力
"--color" 出力を色づけ

無駄を省いて高速にする意味だろう。
spool下は大量にファイルがあるから、-Uは効果が大きいかも。
"-a"オプションを付けるとソートが動いている。

# touch ./testdir/{1..10000}                                                                                             
# diff <(ls -a ./testdir/) <(ls -f ./testdir/) | wc -l
2898

 


★早速プロセス置換を利用!こりゃ便利!

-Fオプション

manでは、さらっと以下しか説明がない。

append indicator (one of */=>@|) to entries

Linuxコマンド【 ls 】ファイルとディレクトリのリストを表示 - Linux入門 - Webkaru
上記URLには、以下記載があり。

うお!こりゃまた便利。

# find / -type f | head -1 | xargs -i ls -dF {}
/proc/sysrq-trigger
# find / -type d | head -1 | xargs -i ls -dF {}
//
# find / -type l | head -1 | xargs -i ls -dF {}
/dev/initctl@
# find / -type p | head -1 | xargs -i ls -dF {}
/run/systemd/inhibit/1.ref|
# find / -type f -executable | head -1 | xargs -i ls -dF {}
/run/log/journal/f9370ed252a14f73b014c1301a9b6d1b/system@40f6700c2dd041cfb22614542a9d6998-0000000000000001-000542cc7deaa560.journal*
# 

 

sed 's/^>*From />&/' "spool/$n"は何をやっているか?

# echo  '>>From ' | sed -e 's/^>*From />&/g'
>>>From 
# 

 


リダイレクトが一文字増えた。これは&がパターンマッチした部分に置換される為。

こんな動きも抑えておく。

# echo  '>>From test' | sed -e 's/^>*From />&/g'
>>>From test
#