Bashスクリプト内でsedコマンドラインパラメータと変数を使用する

Bashスクリプト内でsedコマンドラインパラメータと変数を使用する

複数のコマンドライン引数を必要とするbashスクリプトがあります。この場合、重要なのはテキストファイルである最初の$ 1です。

ヘッダーはかなり長いです。以下はいくつかのフィールドの例です。

COL0___LINE_NUMBER
COL1_AFF_ID
COL2_FULL_NAME
COL3_ADDRESS
BDID
BEST_STATE
COL48_LATITUDE   
COL49_LONGITUDE

ヘッダー行を変更する必要がありますが、次のコードを使用してこれを行うことができます。これはまさに私が望むことをしますが、これがbashスクリプトを最初に書くことであると考えると、以下の出力の変数を保持するスタイルの変更などを歓迎します。

columns=`cat $1 | head -1 |sed 's/-/_/g' |  sed 's/ /_/g' |
    sed 's/COL[0-9]\+_BDID/DROP_BDID/g' | sed 's/COL[0-9]\+_//g' |
    tr '\t' '\n' | tr  "[:lower:]" "[:upper:]"`

注:改行を含むタブ形式は、純粋に列見出しを反映する際の審美的な側面を考慮したものです。これは私自身の読みやすさと vertica create table ステートメントをエコーするスクリプトユーザーの読みやすさのためです。

とにかく、スクリプトで新しいバージョンを使用できるように、列変数をテキストファイルのヘッダー行にしたいと思います。だから私は完全な生のテキストファイルが欲しいいいえこれは元のヘッダー行であり、私が作成したものなので、以下は私のファイルの編集されたバージョンを表します。

col_arr=($columns)
cut_cols = ""

for i in ${!col_arr[@]}; do
    if [[ "${col_arr[$i]}" =~ ^(__LINE_NUMBER|CONFIDENCE|DROP_BDID|LINE_NUMBER|ZIP9|ZIP9|ZIP9MATCH)$ ]]; then
            echo "$i"
            #haven't written yet, but this will add to cut_cols so that 
            #I can remove the above listed columns in the text file 
            #based on their index.
    fi
done
/opt/vertica/bin/vsql -U ${4} -w ${5} -h ${database} \
    -c "copy $schema.$table from STDIN delimiter E'\t' direct no escape;"

答え1

元のシェルパイプラインのすべてのコマンドをcolumns=単一のsedスクリプトにまとめることができます。スクリプトsedは入力の最初の行のみを変更してから終了します。次のアプローチは正確にcolumns=元の質問と同じです。

columns=$(
    sed '               
        1 {                                   # execute block on line 1
            s/-/_/g     
            s/ /_/g     
            s/COL[0-9]\+_BDID/DROP_BDID/g
            s/COL[0-9]\+_//g
            s/\t/\n/g   
            y/abcdefghijklmnopqrstuv/ABCDEFGHIJKLMNOPQRSTUV/
            q                                 # quit after line 1
        }
    ' "$1"
)

# . . .

私は読みやすくするために複数行形式を好みます。もともと文は一行にすぎませんが、はるかに効率が悪く、読みにくいと思います。ヨムド

これで、newlinesで区切られた変数に格納されている入力ファイル(arg 1)のヘッダーがありますcolumns$columnsループを使用して文字列を繰り返すと、列for名は改行文字で区切られますcut_cols

cut_cols="$(
    for col in $columns
    do
        case $col in
        (*__LINE_NUMBER*|*CONFIDENCE*|*DROP_BDID*|*LINE_NUMBER*|*ZIP9*|*ZIP9MATCH*)
                echo "$col"
                ;;
        esac
    done
)"

好みに応じて同じことを行います。

cut_cols=
for col in $columns
do
    case $col in
        (*__LINE_NUMBER*|*CONFIDENCE*|*DROP_BDID*|*LINE_NUMBER*|*ZIP9*|*ZIP9MATCH*)
            cut_cols="$cut_cols $col"
            ;;
    esac
done
cut_cols=$(echo "$cut_cols" | sed 's/^ *//; s/ /\n/g')

cut_cols私はシェル配列を使用していないので、配列ループをテストしていません。上記の反復方法は$columns比較的一般的で伝統的な方法である。 Arraysは拡張であり、すべてのシェルで使用できるわけではありません。

に割り当てた後cut_colsは同じことを繰り返すことができます$columns

元のファイルデータを含む新しいヘッダーを送信するには、新しいヘッダーを印刷してから、元のファイルの最初の行を除いてすべて印刷します。 2つのコマンドの出力を1つのプログラムのように一緒にリダイレクトできるように、コマンドグループ(および{間)でこれを行います。}

以下は、元のヘッダー行なしで完全な生のテキストファイルを生成しますが、生成したヘッダー行を含む次のようstdinに送信しますvsql

# . . .

{                                   # start command group

    echo "$columns" | tr '\n' '\t'; # print with tabs instead of newlines
    echo                            # add newline record separator
    sed 1d "$1"                     # print all but 1st line of "$1"

} |                                 # pipe as one file to vsql

/opt/vertica/bin/vsql -U ${4} -w ${5} -h ${database} \
    -c "copy $schema.$table from STDIN delimiter E'\t' direct no escape;"

答え2

私はこの質問を本当に理解していません。(具体的には、ファイルの列ヘッダー行のみを編集する理由 - 後で識別するために使用されるすべての行はどうなりますか?)しかし、この部分は意味があります。

        #haven't written yet, but this will add to cut_cols so that 
        #I can remove the above listed columns in the text file 
        #based on their index.

わかりました。以下は、sedファイルから特定のフィールドを抽出するためのいくつかのヒントです。

printf 'one    two three' |
sed    's|[^ ]*||5'

one     three

奇妙に見えますが、そうですか?ここでsed5番目は削除されました。可能長さに関係なく、空白以外の文字シーケンスを単一フィールドとして計算する空白以外の文字シーケンス - 長さゼロのシーケンスを含みます。だから一つは最初のフィールドで、次は次のスペースとその後のスペースの間の空の文字列です。フィールド3と4も同様で、5番目のフィールドはスペース4つです。私も知って、大変なことだ。

printf 'one    two three' |
sed    's|[^ ][^ ]*||2'

one     three

確かに各フィールドは空白ではなく、1 つ以上の文字と一致するため、sed他のプログラムと同様に機能します。しかし、正規表現の最大の利点は、特に編集に適用するときに出力動作を非常に具体的にカスタマイズすることができ、空の文字列を処理することはその一部にすぎないということです。

答え3

わかりました、わかりました。一部の人々を混乱させる問題は、ヘッダー行を取得し、フィールド名の奇妙な部分を編集してファイルに再度追加する方法です。

私がしたことは次のとおりです。

  1. ヘッダー行を編集して変数に割り当てます。
  2. 常にヘッダー行とテキストファイルの残りの部分を分離してください。

このソリューションは、主にVerticaテーブルのローダーであるスクリプトの特性に起因します。ヘッダー行とファイルから同じフィールドを削除するだけで、1つのファイルになっても問題はありません。私が最もやりたいことは、編集したタイトルを元のコンテンツと再結合して、正しいタイトル行を含むテキストファイルをディレクトリに保存できるようにすることで、タイトル行とコンテンツを別々に切り取る必要がないようにすることです。ところでこんなに別々に切って出たのですが、

col_arr=($columns)
cut_cols=""

for i in ${!col_arr[@]}; do
    if ! [[ "${col_arr[$i]}" =~ ^(__LINE_NUMBER|CONFIDENCE|DROP_BDID|LINE_NUMBER|ZIP9|ZIP9|ZIP9MATCH)$ ]]; then
            ind=$(($i+1))
            cut_cols="$cut_cols,$ind"
    fi
done

cut_cols=$(echo $cut_cols | sed s/^,//g)
columns=$(echo "$columns" | cut -f "$cut_cols")
cut -f ${cut_cols} ${1}>member_temp.txt
sed -i 1d member_temp.txt

列の変数を保持する決定は、このスクリプトをローダーとして使用することによって行われました。 Vertica でテーブルを作成するには、各フィールドとそのデータ型を識別するステートメントが必要です。これを行うには、createステートメントの構文で使用される文字列のフィールドとデータ型で変数を埋めるいくつかのifステートメントで列変数(ヘッダー行)を実行します。

その後、前に作成したテーブルにmember_temp.txtをロードしました。ヘッダー行がないことは重要ではありません。テーブルに保存したくないので、とにかく削除するからです。

cat member_temp.txt | /opt/vertica/bin/vsql -U ${4} -w ${5} -h ${database} \
-c "copy $schema.$table from STDIN delimiter E'\t' direct no escape;"

関連情報