複数のディレクトリのファイルにsedを使用する

複数のディレクトリのファイルにsedを使用する

複数のディレクトリに複数のファイルがあり、各ファイルの最初の行に間違った日付があります。 sedとforループを含むスクリプトを作成しようとしています。

各ファイルは、正しい日付で構成された独自のディレクトリにあります。たとえば、ファイルが./2014/06/02/record1にあり、最初の行の日付を「2014/06/02」に変更したいとします。

多くのファイルがあり、それぞれに独自のディレクトリがあります。 sedとforループを使用してこれをどのように達成できますか?

答え1

すべてのファイルを繰り返すには、たとえば親ディレクトリにあり、2014ファイル自体が次のように呼び出されるとしますrecord<something>

for pathname in 20[0-9][0-9]/[0-9][0-9]/[0-9][0-9]/record*
do
    if [ -f "$pathname" ]; then
        # ...
    fi
done

このパターンは20[0-9][0-9]/[0-9][0-9]/[0-9][0-9]/record*、前述のすべてのパス名と一致し、2000から2099までの最上位ディレクトリのみがあるとします。

ループ内のテストは、$pathname値が既存の一般ファイルのパス名(またはそのファイルへのシンボリックリンク)であることを確認することです。パターンが何も一致しない場合、デフォルトでは拡張されていません(ほとんどのシェルで)。テストによってこの事実が明らかになります。

$pathname上記のループからディレクトリパスを取得するには:

dirpath=$( dirname "$pathname" )

または

dirpath=${pathname%/*}

このdirnameユーティリティは、指定されたパス名のディレクトリパスである文字列を返します。この変数置換バリアントは、最後の変数のすべてを/削除します$pathname。この場合、両方のコマンドは同じ結果を生成しますが、一般的に使用する方が安全です(変数置換が返されるのではなく、指定された文字列をdirname返します)。aa/b/a/b

atファイルの最初の行を$pathnamein文字列に置き換えるには$dirpath(GNUsedと内部編集を使用):

sed -i -e '1c\' -e "$dirpath" "$pathname"

cin コマンドはsedコンテンツ行を完全に削除し、その場所に別のコンテンツを挿入します。ここでは、最初の行にのみ適用し、$dirnameパス名の日付になる結果文字列を挿入します。

これをスクリプトに統合します。

#!/bin/sh

for pathname in 20[0-9][0-9]/[0-9][0-9]/[0-9][0-9]/record*
do
    if [ -f "$pathname" ]; then
        dirpath=${pathname%/*}
        sed -i -e '1c\' -e "$dirpath" "$pathname"
    fi
done

ファイルのバックアップコピーをテストします。


代替同等の実装を使用する(ほとんど同じであり、通常のファイルへのシンボリックリンクであるファイルをfind処理しません):record*

find 20[0-9][0-9]/ -type f \
    -path '20[0-9][0-9]/[0-9][0-9]/[0-9][0-9]/record*' -exec sh -c '
    for pathname do
        sed -i -e "1c\\" -e "$( dirname "$pathname" )" "$pathname"
    done' sh {} +

これは解決策の最初の変形と本質的に同じループに従う。

答え2

次のスクリプトを試してください。

#!/bin/bash

for YEAR in $(ls -1)
do
    echo -n Processing year $YEAR " "
    for MONTH in $(ls -1 $YEAR)
    do
        echo -n month $MONTH " "
        for DAY in $(ls -1 $YEAR/$MONTH)
        do
            echo -n day $DAY
            sed -i "1 s#^.*#$YEAR/$MONTH/$DAY#" $YEAR/$MONTH/$DAY/record1
        done
    done
    echo " "
done

注:ファイルツリーの上のディレクトリにスクリプトを保存し、次のように実行します。

~/tree-of-files $ ../change-dates.sh 
Processing year 2014  month 06  day 02   
Processing year 2017  month 06  day 02 
Processing year 2018  month 06  day 02 
Processing year 2033  month 06  day 02 
~/tree-of-files $ 

答え3

既存のアプローチは少し異なります。各ファイルでYYYY / MM / DDディレクトリ形式を想定するワイルドカードパターンを使用してファイルを繰り返し、次を使用して最初の行をディレクトリed構造を含む対応する名前に変更します。

for file in [0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]/*
do
  [ -f "$file" ] || continue
  ed -s "$file" <<< $'1c\n'"${file%/*}"$'\n.\nw\nq'
done

コマンドは、ed次のコンポーネントと共にこの文字列として提供されます。

  • 1c- 行1を次に変更します...
  • ${file%/*}- 最後のスラッシュ以降に生成された変数のパラメータ拡張を削除しますfile(タイムスタンプ/ディレクトリ構造部分のみが保持されます)。
  • .--hangecコマンドの終了
  • w--ディスクにファイルを書き込む
  • q——編集終了

関連情報