如何用反斜杠和空格“\”替换空格,以便 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" $aa 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 文件中双引号这些路径之前,它给了我 错误。【参考方案2】:

tsv 文件以空格分隔

至少使用tab,因为tab 出现在路径中的可能性低于space

备注:你知道只有/\0(NULL 字节)是Linux 文件系统中文件名的禁止字符吗?这意味着除了\0 之外的所有内容都可以出现在路径中...

假设您的文件现在以tab 分隔,并且您的路径不包括newlinestabs。在 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"

如果您需要显式转义一个变量(例如在scprsync 的远程部分),那么您可以像这样使用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),或任何其他不属于文字文件名的转义/引号。 tsvcsv 可以有各种不同的非文字引用,需要正确解析。

还要注意rsync 的这种用法,用于将文件名从列表文件复制到单个目标:rsync [opts] —files-from="my-source-list" /my/target/

【讨论】:

以上是关于如何用反斜杠和空格“\”替换空格,以便 bash shell 脚本可以从 .tsv 文件中读取文件名并执行 rsync 复制的主要内容,如果未能解决你的问题,请参考以下文章

如何在bash中将空格替换为新行[重复]

如何用“-”替换多余的空格(不是空格)?

如何用加号“+”号替换空格

如何用Powershell中的字符替换初始字符+空格

./(点斜杠)与. (点空格)运行脚本文件的差别

如何用加号替换变量中的空格。批