Предположим, мне нужно добавить приведенное ниже содержимое в первую ячейку (а не построчно) существующего файла CSV, содержащего сведения о клиенте. Как я могу этого достичь?
Добавляемый контент:
"This is Loganayaki ,she is trying to append the csv file
But she is not able to, she is facing difficulty using shell script
she is seeking help to fix this issue, so that she cab complete her task.
she tried few things which is not helping her"
Customer_File
:
ID,Customer_Name,Cust_ADD
1,A,CBE
2,B,POL
3,C,POL
Я попробовал приведенный ниже код
#!/bin/bash
# File paths
csv_file = "data.csv"
# New content to prepend
new_content = "This is Loganayaki ,she is trying to append the csv file
But she is not able to, she is facing difficulty using shell script
she is seeking help to fix this issue, so that she cab complete her task.
she tried few things which is not helping her"
# Read existing content of the CSV file (excluding the first line)
existing_content=$(tail -n +2 "$csv_file")
# Combine new content with existing content
combined_content = "$new_content"$'\n'"$existing_content"
# Write the combined content back to the CSV file
echo "$combined_content" > "$csv_file"
Он добавляется, но new_content
добавляется в три разные строки, а \n
оказывается пустой строкой.
Мое ожидание
This is Loganayaki ,she is trying to append the csv file
But she is not able to, she is facing difficulty using shell script
she is seeking help to fix this issue, so that she cab complete her task.
she tried few things which is not helping her
ID,Customer_Name,Cust_ADD
1,A,CBE
2,B,POL
3,C,POL
есть ли ссылка, я могу сослаться на
Я имею в виду... вот ссылка, которую я дал в предыдущем комментарии.
Просто чтобы уточнить: вы просто хотите добавить строку в первые строки CSV-файла? Судя по ожидаемому результату, это так и есть.
Я думаю, вы можете добиться ожидаемого результата, заменив tail -n +2 "$csv_file"
просто на cat "$csv_file"
, что даст combined_content = "$new_content"$'\n\n\n'$(cat "$csv_file")
Правильно, я хочу добавить строку в первую ячейку файла csv.
@larsks К вашему сведению, некоторые варианты awk (например, GNU и Kernighans awk) имеют встроенный синтаксический анализ CSV, см. «Какой самый надежный способ эффективного анализа CSV с использованием awk», но OP Вопрос на самом деле, похоже, не имеет ничего общего с CSV, речь идет просто о прикреплении содержимого переменной в верхней части файла (который может быть CSV, но может быть чем-то другим).
Просто заключите $new_content
в «двойные кавычки», так как в вашей строке они не используются. Попробуйте просто: { printf '"%s"\n" "$new_content";cat "$csvFile";} >outfile.csv
или printf '"%s"\n%s\n' "$new_content" "$(<$csvFile)" >outfile.csv
....
Я думаю, что вопрос немного запутан, не в последнюю очередь потому, что то, что вы пишете как ожидаемое содержание, не соответствует словесному описанию того, чего вы хотите достичь. Я бы порекомендовал вам сесть и сначала подумать, как на самом деле должен выглядеть полученный файл, а затем задать новый вопрос.
... Или более многоразовые: printf '"%s"\n%s\n' "${new_content//\"/\"\"}" "$(<$csvFile)" >outfile.csv;
.
Я думаю, что если он просто добавляется к первым строкам файла csv, вам просто нужно заменить existing_content=$(tail -n +2 "$csv_file")
на existing_content=$(cat "$csv_file")
, так как вы хотите сохранить все исходное содержимое файла.
#!/bin/bash
# File paths
csv_file = "data.csv"
# New content to prepend
new_content = "This is Loganayaki ,she is trying to append the csv file
But she is not able to, she is facing difficulty using shell script
she is seeking help to fix this issue, so that she cab complete her task.
she tried few things which is not helping her"
# Read existing content of the CSV file (excluding the first line)
existing_content=$(cat "$csv_file")
# Combine new content with existing content
combined_content = "$new_content"$'\n\n\n'"$existing_content"
# Write the combined content back to the CSV file
echo "$combined_content" > "$csv_file"
Это отредактирует файл так, чтобы он был
This is Loganayaki ,she is trying to append the csv file
But she is not able to, she is facing difficulty using shell script
she is seeking help to fix this issue, so that she cab complete her task.
she tried few things which is not helping her
ID,Customer_Name,Cust_ADD
1,A,CBE
2,B,POL
3,C,POL
Однако это недопустимый вывод CSV.
@tripleee: Полученный файл больше не должен быть действительным CSV. Посмотрите на ожидаемый результат, как указано в вопросе.
Результат, который вы описываете в «Мое ожидание...», может быть достигнут с помощью
if tf=$(mktemp)
then
printf %s "$new_content" >$tf
cat "$csv_file" >>$tf
mv -- "$tf" "$csv_file"
fi
Или — адаптируя идею @EdMorton:
if tf=$(mktemp)
then
{
printf %s "$new_content"
cat "$csv_file"
} >$tf
mv -- "$tf" "$csv_file"
fi
Конечно, файл CSV больше не будет действительным CSV, как упомянул @tripleee в своем комментарии, но это то, о чем вы просили.
Как отмечено в комментариях, ваш ожидаемый результат не соответствует вашему прозаическому описанию того, что вы хотите. Я предполагаю, что это связано с ограниченным пониманием формата CSV; и поэтому я предложу несколько альтернативных решений, которые позволят достичь того, чего, как я полагаю, вы действительно хотите.
Вкратце: чтобы текстовый файл был действительным файлом CSV, он должен удовлетворять нескольким простым ограничениям.
,
, но существует множество распространенных вариантов, таких как TSV, где вместо этого разделителем является символ табуляции, а также варианты, разделенные точкой с запятой, вертикальной чертой и т. д.) Другие поля также могут быть заключены в кавычки, но это совершенно необязательно. Чтобы закодировать буквальный символ-разделитель внутри поля в кавычках, удвойте его.Существуют диалекты с немного другими правилами, но это, безусловно, самые распространенные соглашения.
Итак, чтобы добавить это многострочное значение в поле, вам нужно заключить его в кавычки; и вам необходимо сохранить существующую структуру полей.
(Чтобы упростить следующее изложение, я буду использовать более короткое значение, чем то, которое вам нужно. Мы добавим поле
foo, "bar"
baz!
Вы заметите, что он содержит буквальную запятую, буквальные кавычки и буквальную новую строку.)
Чтобы добавить это значение в первую ячейку без заголовка, файл должен выглядеть следующим образом:
ID,Customer_Name,Cust_ADD
"1foo, ""bar""
baz!",A,CBE
2,B,POL
3,C,POL
Если вы хотите заменить, а не добавить, очевидно, удалите 1
, который был старым значением этой ячейки. Чтобы добавить (или заменить) имя первого поля в строке заголовка, должно быть очевидно, что нужно изменить (т. е. выбрать первую строку вместо второй).
Таким образом, задача состоит в том, чтобы применить необходимые изменения к значению перед манипулированием файлом.
#!/bin/bash
csv_file = "data.csv"
new_content = "This is Loganayaki ,she is trying to append the csv file
But she is not able to, she is facing difficulty using shell script
she is seeking help to fix this issue, so that she cab complete her task.
she tried few things which is not helping her"
# Apply necessary transformations
replacement=${new_content//\"/\"\"}
replacement=${replacement//$'\n'/$'\\\n'}
# Replace
sed -i "2s/\([^,]*\),/\"\1$replacement\",/" "$csv_file"
Простой скрипт sed
заменяет первое поле и запятую после него во второй строке на открывающую кавычку, предыдущее значение (\1
), новый текст, закрывающую кавычку и запятую, эффективно добавляя новый текст к существующему значению. в первой ячейке этой строки.
Если вы хотите заменить вместо добавления, вы можете опустить обратную ссылку, чтобы сохранить предыдущее значение поля.
sed -i "2s/^[^,]*,/$replacement,/" "$csv_file"
И, как отмечалось выше, если вы хотите настроить таргетинг на другую строку, отличную от второй, измените адрес номера строки 2
.
Демо: https://ideone.com/5o54dE
Это довольно хрупко, потому что
Он не будет работать, если текст замены будет содержать косую черту. Вы можете изменить сценарий sed
, чтобы использовать другой разделитель, или выполнить другое преобразование, чтобы добавить обратную косую черту перед каждой буквальной косой чертой. На самом деле, вам нужно сделать это для каждой буквальной обратной косой черты или амперсанда.
replacement=${replacement//[\\/&]/\\&}
При этом используется расширение параметра Bash , которое не переносимо на sh
(тогда как исходный скрипт не использовал синтаксис Bash и, таким образом, вполне мог иметь #!/bin/sh
shebang . Возможно, см. также Разница между sh и ругайся)
Вы заметите, что мы уже используем нечто похожее на обратную косую черту для каждого символа новой строки для sed
. Вышеупомянутую замену необходимо будет выполнить перед той, которая добавляет обратную косую черту перед новой строкой.
Это может привести к необычным поломкам, если существующее поле уже заключено в кавычки. Для этой цели было бы несложно написать немного другое регулярное выражение. (Например, разрешите закрывающую кавычку непосредственно перед запятой и в этом случае опустите начальную кавычку из нового значения. Регулярное выражение должно допускать двойные двойные кавычки или любые символы, которые не являются двойными кавычками, в первом поле. Если вы хотите чтобы реализовать обе возможности, сценарий должен быть немного сложнее, хотя и ненамного.)
Некоторые из вышеперечисленных уродств проистекают из неудачной границы интерфейса между оболочкой и sed
. Вы часто видите скрипты sed
, собранные из строковых переменных оболочки, но это приводит ко многим проблемам, когда вам нужно различными способами массировать строки, чтобы сделать их подходящими для sed
. Учитывая это, для сценариев Awk доступен гораздо более понятный интерфейс; но стандартный Awk не имеет удобного редактирования sed -i
на месте (что тоже не является стандартным, но довольно распространено). Тогда вам просто нужно записать временный файл и переименовать его, чтобы заменить входной файл. Кроме того, синтаксис Awk более подробный (но и менее предназначен только для записи).
t=$(mktemp -t replace.XXXXXXXX) || exit
trap 'rm -f "$t"; exit' ERR EXIT HUP INT
awk -F , -v replace = "$new_content" '
BEGIN { OFS=FS; gsub(/"/, "\"\"", replace) }
FNR==2 { $1 = "\"" $1 replace "\"" } 1' "$csv_file" >"$t" &&
mv "$t" "$csv_file"
Демо: https://ideone.com/DoPH8x
Это потребует более обширного рефакторинга, чтобы правильно обрабатывать цитируемые поля во входных данных.
Однако на этом этапе вместо того, чтобы усложнять сценарий sed
или Awk, возможно, стоит обратиться к Python, чей модуль csv
сделает за вас все это и многое другое.
import csv
import sys
csv_file = "data.csv"
new_content = """This is Loganayaki ,she is trying to append the csv file
But she is not able to, she is facing difficulty using shell script
she is seeking help to fix this issue, so that she cab complete her task.
she tried few things which is not helping her"""
with open(csv_file) as inp:
rows = csv.reader(inp)
writer = csv.writer(sys.stdout)
for lineno, row in enumerate(rows, 1):
if lineno == 2:
row[0] += new_content
writer.writerow(row)
Не пытайтесь прочитать весь входной файл в памяти, просто используйте временный файл:
#!/usr/bin/env bash
tmp=$(mktemp) &&
trap 'rm -f "$tmp"; exit' EXIT &&
{
printf '%s' "$new_content" &&
cat -- "$csv_file"
} > "$tmp" &&
mv -- "$tmp" "$csv_file"
Символы &&
необходимы, чтобы не испортить входной файл, если на предыдущем шаге что-то не удалось. Если вас беспокоит сохранение разрешений и т. д. исходного файла, измените mv -- "$tmp" "$csv_file"
на cat -- "$tmp" > "$csv_file"
.
Используйте язык с более надежными возможностями анализа CSV. Например, в Python есть модуль csv для чтения и записи файлов csv. Это значительно облегчит вашу задачу.