【问题标题】:Reading null delimited strings through a Bash loop通过 Bash 循环读取空分隔的字符串
【发布时间】:2012-01-30 10:38:12
【问题描述】:

我想遍历文件列表而不关心文件名可能包含哪些字符,因此我使用了一个由空字符分隔的列表。代码会更好地解释事情。

# Set IFS to the null character to hopefully change the for..in
# delimiter from the space character (sadly does not appear to work).
IFS=$'\0'

# Get null delimited list of files
filelist="`find /some/path -type f -print0`"

# Iterate through list of files
for file in $filelist ; do
    # Arbitrary operations on $file here
done

以下代码在从文件中读取时有效,但我需要从包含文本的变量中读取。

while read -d $'\0' line ; do
    # Code here
done < /path/to/inputfile

【问题讨论】:

  • 我认为不可能将空字符存储在 bash 变量中。至少,我从来没有找到一种方法来做到这一点......
  • 已确认,bash: warning: command substitution: ignored null byte in input。这是因为 bash 旨在用于 posix 衍生环境,其中 env var 在内部存储在以 null 结尾的缓冲区中,而 bash var(在我检查过的每种情况下)都是宿主 env var。

标签: bash delimiter null-character


【解决方案1】:

最好的方法是使用进程替换

while IFS= read -r -d $'\0' file; do
    # Arbitrary operations on "$file" here
done < <(find /some/path -type f -print0)

如果您一心想要以类似方式解析 bash 变量,则可以这样做只要列表以 NUL 终止。 p>

这是一个 bash var 保存制表符分隔字符串的示例

$ var=$(echo -ne "foo\tbar\tbaz\t"); 
$ while IFS= read -r -d $'\t' line ; do \
    echo "#$line#"; \
  done <<<"$var"
#foo#
#bar#
#baz#

【讨论】:

  • 设置了 -d 标志后,IFS 有什么用?
  • @thisirs 通过将IFS 设置为空字符串,将保留前导和尾随空白字符。
  • 完全同意@joanpau。我不知道 2011 年的东西是如何工作的,但是在 2016 年使用 bash4 这种方法不起作用。如果您在 while 循环之前分配 var=$(find . -print0),您可以轻松验证这是否失败。进程替换确实有效,但变量无效。即使您像var=$(echo -e "some\0text") 这样动态构建变量,也无法将sometext 分开。对于变量,你需要做一个这样的技巧:stackoverflow.com/questions/6570531/…
  • 这不是将 IFS 设置为空字符串。它是unsetting IFS, causing it to use the default values ($' \t\n')
  • @jeremysprofile 实际上,IFS= 正在禁用分词。它unset IFS那样将IFS设置回其默认值。
【解决方案2】:

我尝试使用上面的 bash 示例,最后放弃了,并使用了 Python,它第一次工作。对我来说,事实证明问题在外壳之外更简单。我知道这可能与 bash 解决方案无关,但无论如何我都会将其发布在这里,以防其他人想要替代方案。

import sh
import path
files = path.Path(".").files()
for x in files:
    sh.cp("--reflink=always", x, "UUU00::%s"%(x.basename(),))
    sh.cp("--reflink=always", x, "UUU01::%s"%(x.basename(),))

【讨论】:

  • 在(sh-,而不是 bash-)shell 脚本中遇到了类似的问题。决定用 Perl 重写整个该死的脚本。
【解决方案3】:

使用env -0按零字节输出分配。

env -0 | while IFS='' read -d '' line ; do
    var=${line%%=*}
    value=${line#*=}
    echo "Variable '$var' has the value '$value'"
done

【讨论】:

    【解决方案4】:

    将它们发送到xargs -0:

    files="$( find ./ -iname 'file*' -print0 | xargs -0 )"

    xargs manual:

    -0, --null
        Input items are terminated by a null character instead of
        by whitespace, and the quotes and backslash are not
        special (every character is taken literally).
    

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    【解决方案5】:

    就可读性和可维护性而言,bash 函数可能更简洁:

    使用ffmpegMOV 文件转换为MP4 的示例(适用于包含空格和特殊字符的文件):

    #!/usr/bin/env bash
    
    do_convert () { 
      new_file="${1/.mov/.mp4}"
      ffmpeg -i "$1" "$new_file" && rm "$1" 
    }
    
    export -f do_convert  # needed to make the function visible inside xargs
    
    find . -iname '*.mov' -print0 | xargs -0 -I {} bash -c 'do_convert "{}"' _ {}
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-05
      • 2014-11-10
      • 2012-04-19
      • 2018-09-25
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      • 2021-10-26
      相关资源
      最近更新 更多