特定のシェルスクリプトの `sed` 使用法の説明

特定のシェルスクリプトの `sed` 使用法の説明

読みながらオンラインチュートリアル、次のコードが見つかりました。

#!/bin/bash
# Counting the number of lines in a list of files
# for loop over arguments
# count only those files I am owner of

if [ $# -lt 1 ]
then
  echo "Usage: $0 file ..."
  exit 1
fi

echo "$0 counts the lines of code" 
l=0
n=0
s=0
for f in $*
do
  if [ -O $f ] # checks whether file owner is running the script
  then 
      l=`wc -l $f | sed 's/^\([0-9]*\).*$/\1/'`
      echo "$f: $l"
      n=$[ $n + 1 ]
      s=$[ $s + $l ]
  else
      continue
  fi
done

echo "$n files in total, with $s lines in total"

sedこの例では、呼び出しの目的は何ですか?

答え1

例6のコマンドは、sed出力から行数のみを抽出しますwc -l

実行中ですwc -l$f引数として渡されたスクリプトが所有するファイル)。通常、次のような出力が生成されます。

$ wc -l .bashrc
17 .bashrc

1列の行数と2列のファイル名。このsedコマンドは非常に不要な方法で行数だけを取得します。

$ wc -l .bashrc | sed 's/^\([0-9]*\).*$/\1/'
17

このsedステートメントは's/^\([0-9]*\).*$/\1/'次のことを行います。

  • ^- 行の先頭に一致します。
  • \([0-9]*\)- 数字を無制限に一致させます(角かっこをエスケープするとキャプチャグループが形成されます)。
  • .*- 何でも無制限に一致
  • $- 行末の一致
  • \1- 最初のキャプチャグループの内容を示します。

デフォルトでは、これは数字で始まるすべての行と一致し、行全体を最初のキャプチャグループ(番号)に置き換えます。


これを推薦してくれたStephen Kittに感謝します。

$ wc -l < .bashrc
17

それ以外の場合は、以下のようにcut使用する方が良いです。awk

$ wc -l .bashrc | cut -d' ' -f1
17

$ wc -l .bashrc | awk '{print $1}'
17

答え2

このコードの目的sedは、出力を解析してwc -lファイルの行数を抽出することです。

これは通常必要ありません。

l=$( wc -l <"$f" )

同じことをします(あなたはこれを試す必要があります)。


スクリプトは移植可能ではなく、「古い」と見なされるいくつかの構成を使用し、スクリプトには安全でないようにするいくつかの詳細があります。

  1. 拡張子を引用する必要があります。たとえば、if [ $# -lt 1 ]を書くほうがいいですし、書く必要がif [ "$#" -eq 0 ]ありif [ -O $f ]ますif [ -O "$f" ]。これにより、すべての文字、さらにはその中の文字$IFS(スペース、タブ、および改行)を含むファイル名をサポートできます。何らかの理由で数字を含める$#場合は、$IFS引用符で囲む必要があります。

    このコンテンツの詳細については、次の3つの質問をご覧ください。bash / POSIXシェルで変数を引用することを忘れてしまうセキュリティリスク「、」スペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?「そして」いつ二重引用符が必要ですか?」。

  2. 場合によっては、コマンドの置き換えにバックティックを使用するのは面倒です。その行はl=`wc -l ...`次のように書き直すことができますl=$(wc -l ...)。最新の行は入れ子になっており、参照が期待どおりに機能し(たとえば、構文エラーを生成するのと比較して)、読みやすくなるため、より$(...)良いです。echo "`echo "`echo ok`"`"echo "$(echo "$(echo ok)")"

    これについて詳しくは、「* shシェルではバックティック(「cmd」など)は使用されなくなりましたか?

  3. $[ $s + $l ]移植できない文です$(( s + l ))

  4. printf変数データはを使用する代わりにを使用して出力する必要がありますecho。たとえば、最後の行は

    echo "$n files in total, with $s lines in total"
    

    次のように書き換えることができます。

    printf '%d files in total, with %d lines in total\n' "$n" "$s"
    

    たとえば、なぜprintfがechoより優れているのですか?」。

  5. ループを使用して$*コマンドライン引数を繰り返すと、スペースを含むファイル名からスクリプトは実行されません。

  6. そのcontinueステートメントとelseそのステートメントの分岐はとにかくループの終わりにあるため、まったく必要ありません。if

  7. 診断出力は標準エラーで印刷する必要があります。

スクリプトの「修正済み」バージョン:

#!/bin/bash
# Counting the number of lines in a list of files
# for loop over arguments
# count only those files I am owner of

if [ "$#" -eq 0 ]; then
    printf 'Usage: %s file ...\n' "$0" >&2
    exit 1
fi

printf '%s counts the lines of code\n' "$0"
l=0; n=0; s=0
for name do
    if [ -f "$name" ] && [ -O "$name" ]; then # checks whether its a regular file and file owner is running the script
        nlines=$( wc -l <"$name" )
        printf '%s: %d\n' "$name" "$nlines"
        totlines=$(( totlines + nlines ))
        nfiles=$(( nfiles + 1 ))
    fi
done

printf '%d files in total, with %s lines in total" "$nfiles" "$totlines"

関連情報