web-dev-qa-db-ja.com

bashの区切り文字を使用して複数のファイル名からファイル名を抽出する関数を作成する

たとえば、レポートjpgは、something.jpgと呼ばれるすべてのファイルについて表示されます。これが私の質問を含む私が取り組んでいるコードです。

function report {
        x=`ls *.$1`
        num=`ls *.$1 | wc -l`  
        # Question 1:
        # Want to use variable for x to get
        # num, but num=$($x | wc -l) didn't work for this purpose.
        echo There are $num $1 files
        if [ $num -lt 10 ]; then
            # Question 2:
            # for this part, I want to use "find . -name '*.$1' 
            # with delimiter . to extract filename from its extension 
            # and display them (Also maybe need to use while or for loop)              
        fi
}
report jpg
report html
2
Rachael
#!/bin/bash

report(){

    local -a n=(*.$1)
    echo "There are #${#n[@]} files in '$PWD' with extension '$1'."

    if (( ${#n[@]} < 10 )); then
        find . -maxdepth 1 -type f -name "*.$1" -printf %f\\n | sed -e 's/\.[^.]*$//'
    fi
}

report $1

使用法:./script.sh jpg

1
bac0n

具体的に質問に答えるには:

Q1。式$x | wc -lは、「コマンド$xを実行し、結果をwc -lにパイプする」ことを意味します。 $xcontentswcにパイプするには、num=`echo "$x" | wc -l`または(bash here string を使用)num=`wc -l <<< "$x"`を使用できます。

ただし、「バックティック」形式のコマンド置換は非推奨になりました。$( ... )を使用する習慣を身に付けた方がよいでしょう。

num=$(wc -l <<< "$x")

ファイル名に改行文字(Linuxでは許可されている)が含まれている場合、これは誤ったカウントを与えることに注意してください。

Q2。 find . -name '*.$1'では、'*.$1'を囲む単一引用符(別名「ハードクォート」)は$1の展開を防ぐため、findは文字どおり$1で終わるファイル名を検索します。 $1の早期展開を防ぎながら*.の展開を許可するには、二重引用符(別名「ソフトクォート」)、つまり"*.$1"を使用します。

サブディレクトリに降りるのではなく、トップレベルのディレクトリに検索を制限したい場合があります(そうしないと、結果がwcから取得したカウントと一致しなくなります)。

    find . -maxdepth 1 -name "*.$1"

ただし、これが私がやる方法です-bashシェルを想定:

report() {
  shopt -s nullglob
  local ext="$1"
  set -- *."$ext"
  printf 'There are %d %s files' $# "$ext"
  if (( $# > 0 && $# < 10 )); then 
    printf '\t%s\n' "$@"
  fi
}

これは Bash Pitfall#1 (シェルグロブを使用する代わりにlsの出力を解析する)を回避し、シェルのsetビルトインを使用して一致するファイル名を$@位置に読み取りますパラメータ配列。このように、$#を使用してファイルの数を明確にカウントできます(たとえ改行が含まれている場合でも、lswcにパイプすると誤ったカウントが発生します)。 findコマンドを分離して、一致する名前を繰り返し取得します。

ファイル名なし拡張子を表示する場合は、printf$@${@%.*}で置き換えることができます。これにより、配列の各要素から.*に一致する最も短い末尾の部分文字列を削除します パラメータ展開

ファイルの数をテストするために(( ... ))算術構造を使用することを選択しましたが、必要に応じてPOSIX [ $# -le 10 ]または[ $# -lt 10 ]を使用しても何も問題はありません。追加の$# > 0テストは、指定された拡張子のファイルがない場合に出力をきれいに保つためのものです。


拡張機能の配列を使用することにより、複数の拡張機能を受け入れるように関数を簡単に拡張できます。

function report {
  shopt -s nullglob
  local exts=( "$@" )
  for ext in "${exts[@]}"; do 
    set -- *."$ext"
    printf 'There are %d %s files' $# "$ext"
    if (( $# > 0 && $# < 10 )); then
      printf ': \n' 
      printf '\t%s\n' "$@"
    else
      printf '.\n'
    fi
  done
}

使用例:

$ report txt jpg foo
There are 71 txt files.
There are 4 jpg files: 
    b.jpg
    foo.jpg
    inv_logpolar.jpg
    logpolar.jpg
There are 0 foo files.
3
steeldriver