如何在 bash shell 中使用 sed/awk 在 .tsv 文件中向 s3 存储桶路径添加双引号

如何在 bash shell 中使用 sed/awk 在 .tsv 文件中向 s3 存储桶路径添加双引号

我有.tsv一个文件,其中包含 s3 存储桶的源和目标信息。我正在使用 while 循环从该文件读取源路径和目标路径并执行s3 cp操作。请注意,该文件包含 100K 行。

如何使用sed命令使文件中的所有源路径和目标路径都变成双引号 ( xx.tsv)。我需要双引号,因为 aws s3 无法处理其中包含空格的文件/文件夹名称,而不用"".

我正在寻找类似以下 3 行的内容将被更改

s3://data01/repo01/image live01.png s3://Ata01/vol01/image live01.png
s3://data02/repo01/image live01.png s3://Ata02/vol01/image live01.png
s3://data03/repo01/image live01.png s3://Ata03/vol01/image live01.png

"s3://data01/repo01/image live01.png" "s3://Ata01/vol01/image live01.png"
"s3://data02/repo01/image live01.png" "s3://Ata02/vol01/image live01.png"
"s3://data03/repo01/image live01.png" "s3://Ata03/vol01/image live01.png"

答案1

在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ awk -F'\t' -v OFS='"\t"' '{print "\"" $1, $2 "\""}' file
"s3://data01/repo01/image live01.png"   "s3://Ata01/vol01/image live01.png"
"s3://data02/repo01/image live01.png"   "s3://Ata02/vol01/image live01.png"
"s3://data03/repo01/image live01.png"   "s3://Ata03/vol01/image live01.png"

上面假设您的文件名都不包含制表符、换行符或双引号。

答案2

$ cat input.tsv 
s3://data01/repo01/image live01.png     s3://Ata01/vol01/image live01.png
s3://data02/repo01/image live01.png     s3://Ata02/vol01/image live01.png
s3://data03/repo01/image live01.png     s3://Ata03/vol01/image live01.png

注意:列之间用制表符分隔,而不是多个空格。

sed

$ sed -E 's/^(s3:.*)\t+(s3:.*)/"\1"\t"\2"/' input.tsv 
"s3://data01/repo01/image live01.png"   "s3://Ata01/vol01/image live01.png"
"s3://data02/repo01/image live01.png"   "s3://Ata02/vol01/image live01.png"
"s3://data03/repo01/image live01.png"   "s3://Ata03/vol01/image live01.png"

在两个捕获组之间使用\s+或代替也可以,并产生相同的输出。即[[:blank:]]+\t

sed -E 's/^(s3:.*)[[:blank:]]+(s3:.*)/"\1"\t"\2"/' input.tsv

sed -E 's/^(s3:.*)\s+(s3:.*)/"\1"\t"\2"/' input.tsv

版本[[:blank:]]+将匹配一个或多个空格或制表符作为列分隔符,而版本\s+将匹配一个或多个任何空白字符(包括空格、制表符等)。

awk

$ awk -F'\t' '{print "\"" $1 "\"\t\"" $2 "\""}' input.tsv 
"s3://data01/repo01/image live01.png"   "s3://Ata01/vol01/image live01.png"
"s3://data02/repo01/image live01.png"   "s3://Ata02/vol01/image live01.png"
"s3://data03/repo01/image live01.png"   "s3://Ata03/vol01/image live01.png"

答案3

将数据视为使用制表符作为字段分隔符的 CSV 文件:

csvformat -tT -U1 file.tsv >newfile.tsv

这使用csvformat来自csvkit读取制表符分隔的输入 ( -t) 并生成制表符分隔的输出 ( ) ,无论 CSV 格式是否需要,该输出都会被-T引用 ( )。-U1

将文件视为 CSV 文件并使用 CSV 解析器为您进行引用的好处是,如果字段已被引用,则不会重复引用字段。

$ cat file.tsv
"s3://data01/repo01/image live01.png"   s3://Ata01/vol01/image live01.png
s3://data02/repo01/image live01.png     s3://Ata02/vol01/image live01.png
s3://data03/repo01/image live01.png     s3://Ata03/vol01/image live01.png
$ csvformat -tT -U1 file.tsv
"s3://data01/repo01/image live01.png"   "s3://Ata01/vol01/image live01.png"
"s3://data02/repo01/image live01.png"   "s3://Ata02/vol01/image live01.png"
"s3://data03/repo01/image live01.png"   "s3://Ata03/vol01/image live01.png"

显然,您也可以直接读取数据,并在调用时添加双引号s3 cp。我不知道该命令是什么样的,但是......

while IFS=$'\t' read -r src dst; do
    s3 cp "\"$src\"" "\"$dst\""
done <file.tsv

答案4

剥土豆皮的方法有很多,但我的是

sed 's;^\(s3://.*\) \(s3://.*\)$;"\1" "\2";' filename_in.tsv > out.tsv

它用于sed在输入上应用正则表达式,捕获字符串中所有“不是 s3-URL 之间的空格”部分,然后将它们放入引号中。

我希望任何编写生成 .tsv 的工具的人都能从中学到一些东西 - 例如,如果文件名中存在换行符,这也可能会出现不可挽回的错误,这对于文件名来说是完全合法的(可能不在 S3 上,没查过)。

将文件名存储在“无论什么分隔符”的文件中都会以糟糕的方式结束(除非该分隔符是 0 字节,这几乎是文件名中唯一禁止的字节)。您需要转义,或者更好的是,不要在文本文件中存储长的文件名列表,而是使用简单的 SQLite 来使用。

相关内容