У меня есть скрипт bash, который выводит два столбца CSV. Мне нужно добавить трехзначное число тех строк второго столбца, которые содержат их, с «f.», а остальные строки оставить нетронутыми. До сих пор я пробовал разные способы, но каждый потерпел неудачу в той или иной степени.
В основном я пробовал использовать регулярные выражения либо с первым, либо со вторым столбцом, чтобы отделить нужные строки от остальных, но я не могу отделить и добавить в начало одновременно, не отменив или не испортив процесс каким-либо образом. Некоторые из команд, которые я использовал до сих пор, были следующими: не удалось) решение:
for var1 in "^.*_[^f]_.*"
do
sed -i "" "s:$MSname::" $pathToCSV"_final.csv"
for var2 in "^.*_f_.*"
do
sed -i "" "s:$MSname:f.:" $pathToCSV"_final.csv"
done
done
И это несколько примеров строк:
abc_deg0014_0001_a_1.tif,British Library 1 Front Board Outside
abc_deg0014_0002_b_000.tif,British Library 1 Front Board Inside
abc_deg0014_0003_f_001r.tif,British Library 1 001r
abc_deg0014_0004_f_001v.tif,British Library 1 001v
…
abc_deg0014_0267_f_132r.tif,British Library 1 132r
abc_deg0014_0268_f_132v.tif,British Library 1 132v
abc_deg0014_0269_y_999.tif,British Library 1 Back Board Inside
abc_deg0014_0270_z_1.tif,British Library 1 Back Board Outside
Здесь $MSname = Британская библиотека 1 (поскольку с разными CSV часть «Британская библиотека 1» может измениться на другие слова, которые мне нужно удалить/заменить, и поэтому я использую расширение параметра).
Желаемый результат:
abc_deg0014_0002_b_000.tif,Front Board Inside
abc_deg0014_0003_f_001r.tif,f. 001r
…
abc_deg0014_0268_f_132v.tif,f. 132v
abc_deg0014_0269_y_999.tif,Back Board Inside
Если вы посмотрите внимательно, вы заметите, что эти строки также отличаются от остальных по «ф» в их первом столбце (строки, которые не должны иметь «f.» перед своим вторым столбцом, отличаются «а» , «б», «у» и «г» соответственно в первом столбце).
Цикл for по одной строке эквивалентен простому присвоению строки переменной цикла, с той незначительной разницей, что вы можете break выйти из цикла, чтобы перейти непосредственно к строке после done (которую вы все равно здесь не используете).
Я отказался от циклов for, но вы правы.





С awk вы можете посмотреть на четвертое поле, чтобы увидеть, соответствует ли оно «3 цифры + 1 буква», затем напечатать с f. в этом случае и просто удалить поля 2,3 и 4 в другом случае. Например:
awk -F'[, ]' '{
if ($5 ~ /.?[[:digit:]]{3}[a-z]$/) {
printf("%s,f. %s\n",$1,$5)}
else {
printf("%s,%s %s %s\n",$1,$5,$6,$7)
}
}' test.txt
В приведенном вами примере это дает:
abc_deg0014_0001_a_1.tif,Front Board Outside
abc_deg0014_0002_b_000.tif,Front Board Inside
abc_deg0014_0003_f_001r.tif,f. 001r
abc_deg0014_0004_f_001v.tif,f. 001v
abc_deg0014_0267_f_132r.tif,f. 132r
abc_deg0014_0268_f_132v.tif,f. 132v
abc_deg0014_0269_y_999.tif,Back Board Inside
abc_deg0014_0270_z_1.tif,Back Board Outside
Это имеет некоторые потенциально неудачные предположения о фиксированном количестве полей, разделенных пробелами.
Конечно. альтернативой было предположить, что строка British Library 1 фиксирована, и заменить ее, как вы предлагаете. Тем не менее, ваше решение можно рассматривать как более ограниченное, поскольку в дополнение к количеству полей оно требует исправления содержимого. Это действительно зависит от фактических данных и потребностей.
Вы ни для чего не используете var1 или var2, а даже если и использовали, то циклическое перебор переменных и повторный запуск sed -i в одном и том же выходном файле крайне расточительны. В идеале вы хотели бы записать все изменения в один скрипт sed и обработать файл только один раз.
Не имея возможности угадать, какие у вас есть другие строки, кроме "British Library 1", и требуют ли они других действий, я бы предложил что-то вроде
sed -i '/^[^,]*_f_[^,_]*,/s/,British Library 1 /,f. /
s/,British Library 1 /,/' "${pathToCSV}_final.csv"
Обратите внимание, как скрипт sed в одинарных кавычках может быть заключен в несколько физических строк. Первая строка находит все строки, в которых последними символами между символами подчеркивания в первом столбце, разделенном запятыми, являются f, и заменяет ",British Library 1 " на ",f. ". (Здесь я внес некоторые изменения в интервалы — надеюсь, они имеют смысл для вас.) В следующей строке мы просто заменяем любые (оставшиеся) вхождения ",British Library 1 " запятой; идея состоит в том, что только строки, которые не соответствуют регулярному выражению в предыдущей строке, по-прежнему будут содержать эту строку, и поэтому нам не нужно выполнять еще одно совпадение с регулярным выражением.
Это можно легко расширить, чтобы охватить больше шаблонов в одном и том же скрипте sed, вместо того, чтобы многократно перебирать файл и переписывать один шаблон за раз. Например, если ваша следующая задача — заменить Windsor Palace A либо на a., либо ничем, в зависимости от того, содержит ли предпоследнее разделенное подчеркиванием подполе в первом поле a, это должно быть достаточно очевидно:
sed -i '/^[^,]*_f_[^,_]*,/s/,British Library 1 /,f. /
s/,British Library 1 /,/
/^[^,]*_a_[^,_]*,/s/,Windsor Palace A /,a. /
s/,Windsor Palace A /,/' "${pathToCSV}_final.csv"
Более подробно регулярное выражение говорит
^ beginning of line
[^,]* any sequence of characters which are not a comma
_f_ literal characters underscore, f, underscore
[^,_]* any sequence of characters which are not a comma or an underscore
, literal comma
Вы должны увидеть, что это будет нацелено на последнюю пару символов подчеркивания в первом столбце. Важно никогда не пропускать первую запятую и ближе к концу не допускать никаких символов подчеркивания после тех, на которые мы специально нацеливаемся, прежде чем мы, наконец, разрешим разделитель столбца запятой.
Наконец, также обратите внимание, как мы всегда используем двойные кавычки вокруг переменных, содержащих имена файлов. Есть сценарии, в которых этого можно избежать, но вы должны знать, что делаете; Простое и понятное практическое правило заключается в том, чтобы всегда заключать переменные в двойные кавычки. Полную информацию см. в статье Когда заключать в кавычки переменную оболочки?.
Фантастический! Это сработало отлично. Только мне пришлось изменить одинарные кавычки на двойные (а также переместить другую открывающую двойную кавычку в заголовке CSV после ее собственной $pathToCSV), поскольку я использую расширение параметра внутри (т.е. $MSname у меня было в моем исходном посте), который заботится о строке переменной «Британская библиотека 1»: echo MS name as recorded in TIFF header to be removed: read MSname sed -i "" "/^[^,]*_f_[^,_]*,/s/,$MSname /,f. / s/,$MSname /,/" $pathToCSV"_final.csv" PS: мне также пришлось добавить "" после флага -i из-за (не)совместимости синтаксиса моей оболочки.
_final.csv не обязательно должен быть в кавычках, но переменная непосредственно перед ним очень часто. Я поместил все это в кавычки для удобства; но нет разумного способа переместить часть это за пределы кавычек. Переключение одинарных кавычек на двойные имеет смысл, если вы хотите использовать переменную внутри кавычек; но правильным решением было бы удалить все циклы оболочки и создать один скрипт sed, который содержит все подстановки, которые вы хотите выполнить.
Это работает в любом случае (я пробовал оба варианта, то есть только с переменной внутри кавычек, как вы предложили в своем комментарии выше, а затем с фиксированной частью _final.csv внутри кавычек, как у меня было изначально). Это дает мне ошибку undefined label, когда это разделение (между переменной и фиксированной частью) удаляется, что имеет смысл. Кажется, что имеет значение именно разделение, а не место вставки кавычек, поэтому, пока я сохраняю переменную отличной от фиксированной части заголовка CSV, так или иначе, все в порядке.
Вот почему я заключаю имя переменной в фигурные скобки; без них $pathToCSV_final ищет переменную с именем pathToCSV_final. Пока значение не содержит пробелов или метасимволов оболочки, вы можете обойтись без его кавычек, но (как более подробно объяснено в вопросе цитирования, на который я ссылался) оно склонно вызывать ошибки, когда вы пытаетесь использовать его в реальных условиях. world, которые могут содержать и то, и другое, и обычно их трудно отлаживать, особенно если вы не знакомы со сценарием оболочки.
Вы устанавливаете свою переменную
var1в строку^.*_[^f]_.*и аналогичноvar2, но никогда не используете эти переменные. Какой смысл их иметь?