如何用反斜杠和空格“\”替换空格,以便 bash shell 脚本可以从 .tsv 文件中读取文件名并执行 rsync 复制
Posted
技术标签:
【中文标题】如何用反斜杠和空格“\\”替换空格,以便 bash shell 脚本可以从 .tsv 文件中读取文件名并执行 rsync 复制【英文标题】:How to replace a space with backslash and space "\ " so bash shell script can read the file name from a .tsv file and perform rsync copy如何用反斜杠和空格“\”替换空格,以便 bash shell 脚本可以从 .tsv 文件中读取文件名并执行 rsync 复制 【发布时间】:2021-12-16 13:52:27 【问题描述】:我有一个脚本,它从一个用空格分隔的 tsv 文件中获取源和目标信息。第一列表示源文件路径,第二列是目标。我的 rsync 命令读取源和目标信息并执行复制操作。
但问题是源文件和目标文件都包含带空格的文件名(header-background copy.jpg),我们知道当 bash shell 读取文件名(带空格)时,它会用反斜杠替换空格,然后空格“\”
/data-prod/bigdata/abc/test/1143-1003-004_1143-1003-905/static/common/images/header-background copy.jpg /mapped-data/data20/data3/header-background copy.jpg
我的问题是如何用“\”替换空格,以便 shell 可以读取它。我尝试使用以下 sed 命令
sed -r 's/^\s+//;s/\s+/\\ /g' test2.tsv
但存在一个问题,因为上面的 sed 命令还在源路径后添加了一个反斜杠。正如我所提到的,我的脚本从 .tsv 文件中获取源和目标信息,因此添加斜杠是一个问题。下面是 sed 命令的输出。
/data-prod/bigdata/abc/test/1143-1003-004_1143-1003-905/static/common/images/header-background\ copy.jpg\ /mapped-data/data20/data3/header-background\ copy.jpg
我想要的是隐蔽的
/data-prod/bigdata/abc/test/1143-1003-004_1143-1003-905/static/common/images/header-background copy.jpg /mapped-data/data20/data3/header-background copy.jpg
到
/data-prod/bigdata/abc/test/1143-1003-004_1143-1003-905/static/common/images/header-background\ copy.jpg /mapped-data/data20/data3/header-background\ copy.jpg
【问题讨论】:
and as we know that when bash shell reads a file name (with space), it replace the space with backslash followed by space “\ ”
不,它不会那样工作。 like covert from
你确定你只想转换字符串吗?为什么不运行 rsync 呢?传递带有\
的字符串仍然会在空格上分割,\ 是无关紧要的。试试看:a='abc\ def' ; printf "%s\n" $a
a tsv file separated by space
原来是TSV文件,所以字段用制表符分隔?如果是这样,请在选项卡上拆分字符串并使用两个变量。
@KamilCuk 可能需要对字符串进行显式转义,例如 scp file.txt user@server:'path\ with\ space/'
TSV 文件的标准转义机制是使用引号。反斜杠在常规 TSV 文件中没有特殊意义。如果您想使用标准格式,可能要遵守其约定,而不是自行破解。
【参考方案1】:
使用sed
,一种方法是对匹配进行分组并返回它,并在后面添加一个反斜杠
sed 's/\([A-Za-z0-9\/][^\.]*\) /\1\\ /g' input_file
/data-prod/bigdata/abc/test/1143-1003-004_1143-1003-905/static/common/images/header-background\ copy.jpg /mapped-data/data20/data3/header-background\ copy.jpg
【讨论】:
非常感谢HatLess!这正是我正在寻找的(使用流编辑器的解决方案)。 @mht.haque 此解决方案有其局限性:每个路径只能有一个空格,并且文件名必须具有扩展名。不过,这可能足以满足您的需求 @Fravadona 我也意识到了这一点。此 sed 解决方法适用于我的 rsync 用例,但不适用于 aws s3。由于 aws s3 cp 命令需要将这些路径双引号。在 tsv 文件中双引号这些路径之前,它给了我tsv 文件以空格分隔
至少使用tab
,因为tab
出现在路径中的可能性低于space
。
备注:你知道只有/
和\0
(NULL 字节)是Linux 文件系统中文件名的禁止字符吗?这意味着除了\0
之外的所有内容都可以出现在路径中...
假设您的文件现在以tab
分隔,并且您的路径不包括newlines
或tabs
。在 BASH 中阅读它的方法如下:
while IFS=$'\t' read -r filepath1 filepath2
do
declare -p filepath1 filepath2
done <<< "/data-prod/bigdata/abc/test/1143-1003-004_1143-1003-905/static/common/images/header-background copy.jpg"$'\t'"/mapped-data/data20/data3/header-background copy.jpg"
输出:
declare -- filepath1="/data-prod/bigdata/abc/test/1143-1003-004_1143-1003-905/static/common/images/header-background copy.jpg"
declare -- filepath2="/mapped-data/data20/data3/header-background copy.jpg"
如果您需要显式转义一个变量(例如在scp
或rsync
的远程部分),那么您可以像这样使用printf '%q'
:
rsync -av "$filepath1" user@server:"$(printf '%q' "$filepath2")"
【讨论】:
【参考方案3】:使用参数替换拆分列:
while IFS= read -r line; do
# count raw tabs to validate format
tabs=$line//[!$'\t']
tabs=$#tabs
if ((tabs!=1)); then
echo "$line//$'\t'/TAB: less or more than one raw tab, skipping…" >&2
else
# split by tab
source=$line%$'\t'*
target=$line#*$'\t'
rsync "$source" "$target"
fi
done < list.tsv
循环检查每一行是否只有一个制表符,如果没有则跳过它。添加您的rsync
选项等。
确保列表没有符号转义字符(如 \t
或 \n
),或任何其他不属于文字文件名的转义/引号。 tsv
和 csv
可以有各种不同的非文字引用,需要正确解析。
还要注意rsync
的这种用法,用于将文件名从列表文件复制到单个目标:rsync [opts] —files-from="my-source-list" /my/target/
【讨论】:
以上是关于如何用反斜杠和空格“\”替换空格,以便 bash shell 脚本可以从 .tsv 文件中读取文件名并执行 rsync 复制的主要内容,如果未能解决你的问题,请参考以下文章