Использование аргумента командной строки и переменной с sed внутри скрипта Bash

Использование аргумента командной строки и переменной с sed внутри скрипта Bash

У меня есть скрипт 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"
)

# . . .

Я предпочитаю многострочный формат также из-за удобства чтения. Хотя исходное утверждение было в одну строку, оно было гораздо менее эффективным, и, по моему мнению, более трудным для чтения. yomd

Теперь у вас есть заголовки из входного файла (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.

Чтобы отправить новый заголовок с исходными данными файла, напечатайте новый заголовок, а затем напечатайте все, кроме первой строки исходного файла. Сделайте это в группе команд (между {и }), чтобы можно было перенаправить вывод обеих команд вместе, как если бы они были одной программой.

Следующий код создает полный исходный текстовый файл без исходной строки заголовка и с той, которую вы создали, и отправляет его 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

Выглядит странно, да? Здесь sedудаляется 5-йвозможныйпоследовательность не-пробельных символов, которая позволяет считать любую длину последовательности не-пробельных символов как одно поле - включая последовательность нулевой длины. И такодин— это первое поле, следующее — это нулевая строка между следующим пробелом и пробелом, который следует за ним, и то же самое для полей 3 и 4, а пятое поле — это 4 пробела. Довольно коряво, я знаю.

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

one     three

Там я включаюопределенныйсоответствует по крайней мере одному символу not-space на поле, и поэтому sedведет себя более похоже на некоторые другие программы. Однако удобная вещь в регулярных выражениях, и особенно при применении к редактированию, заключается в том, что вы можете очень точно настроить поведение вашего вывода, и обработка нулевых строк — это всего лишь часть этого.

решение3

Хорошо, я разобрался с этим. Вопрос, который смутил некоторых, был в том, как мне взять строку заголовка, отредактировать некоторые странности в именах полей и добавить обратно в файл.

Что я в итоге сделал:

  1. Отредактируйте строку заголовка и присвойте ее переменной.
  2. Всегда сохраняйте строку заголовка и оставшийся текстовый файл отдельно.

Это решение в значительной степени обусловлено природой скрипта как инструмента загрузки для таблицы Vertica. Пока одни и те же поля вырезаются из строки заголовка и файла, не имеет значения, будут ли они когда-либо снова одним файлом. Я в основном хотел воссоединить отредактированный заголовок с его исходным содержимым, чтобы я мог сохранить текстовый файл с правильной строкой заголовка в моем каталоге и чтобы мне не пришлось вырезать строку заголовка и содержимое по отдельности. Однако в итоге я вырезал их по отдельности, вот так,

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 требует оператора, который идентифицирует каждое поле и его тип данных. Я делаю это, пропуская переменную columns (строку заголовка) через некоторые операторы if, которые заполняют переменную полями и типами данных в строке, которая будет использоваться в синтаксисе для оператора create.

Затем я просто загрузил 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;"

Связанный контент