
У меня есть скрипт 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
является более универсальным и традиционным методом. Array
s — это расширение, доступное не в каждой оболочке.
После того, как вы присвоили значение 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
Хорошо, я разобрался с этим. Вопрос, который смутил некоторых, был в том, как мне взять строку заголовка, отредактировать некоторые странности в именах полей и добавить обратно в файл.
Что я в итоге сделал:
- Отредактируйте строку заголовка и присвойте ее переменной.
- Всегда сохраняйте строку заголовка и оставшийся текстовый файл отдельно.
Это решение в значительной степени обусловлено природой скрипта как инструмента загрузки для таблицы 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;"