【问题标题】:How to join multiple lines of file names into one with custom delimiter?如何使用自定义分隔符将多行文件名合并为一个?
【发布时间】:2011-02-15 09:11:08
【问题描述】:

我想将ls -1 的结果合并到一行中,并用我想要的任何内容进行分隔。

我可以使用任何标准的 Linux 命令来实现这一点吗?

【问题讨论】:

    标签: linux bash shell parsing merge


    【解决方案1】:

    如果您的 xargs 版本支持 -d 标志,那么这应该可以工作

    ls  | xargs -d, -L 1 echo
    

    -d 是分隔符标志

    如果你没有-d,那么你可以试试下面的

    ls | xargs -I {} echo {}, | xargs echo
    

    第一个 xargs 允许您指定分隔符,在此示例中为逗号。

    【讨论】:

    • -d 使用 GNU xargs 指定输入分隔符,因此不起作用。第二个示例展示了与此处的其他解决方案相同的问题,即末尾的杂散定界符。
    【解决方案2】:

    编辑:如果您希望分隔符为逗号,只需“ls -m”即可

    啊,强大而简单!

    ls -1 | tr '\n' ','
    

    将逗号“,”更改为您想要的任何内容。请注意,这包括“尾随逗号”

    【讨论】:

    • +1,但更精细的版本应该以不同的方式处理 last \n
    • 如果文件名中包含\n,这也将替换它。
    • @ShreevatsaR:我相信他的意思是不附加尾随“,”。像这样ls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
    • @Chris:你的 sed 使用结束标记字符可能会更有效一些:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
    • tr 之后使用sed 似乎只是删除最后一个符号似乎不合理。我选择ls -1 | tr '\n' ',' | head -c -1
    【解决方案3】:

    你可以使用:

    ls -1 | perl -pe 's/\n$/some_delimiter/'
    

    【讨论】:

    • 这不排除尾随分隔符。
    【解决方案4】:

    为了避免 tr 潜在的换行混淆,我们可以在 ls 中添加 -b 标志:

    ls -1b | tr '\n' ';'
    

    【讨论】:

      【解决方案5】:

      这用换行符替换最后一个逗号:

      ls -1 | tr '\n' ',' | sed 's/,$/\n/'
      

      ls -m 在屏幕宽度字符处包含换行符(例如第 80 个)。

      主要是 Bash(只有 ls 是外部的):

      saveIFS=$IFS; IFS=$'\n'
      files=($(ls -1))
      IFS=,
      list=${files[*]}
      IFS=$saveIFS
      

      在 Bash 4 中使用 readarray(又名 mapfile):

      readarray -t files < <(ls -1)
      saveIFS=$IFS
      IFS=,
      list=${files[*]}
      IFS=$saveIFS
      

      感谢 gniourf_gniourf 的建议。

      【讨论】:

      • 这不会处理名称中带有空格的文件。试试这个:dir=/tmp/testdir; rm -rf $dir && mkdir $dir && cd /$dir && touch "这是一个文件" this_is_another_file && ls -1 && files=($(ls -1)) && list=${files[@]/%/ ,} && list=${list%*,} && echo $list
      • @dimir:这个问题的许多答案都存在这个问题。我已经编辑了我的答案以允许使用制表符或空格的文件名,但不允许使用换行符。
      • 您的 bash 版本也受到路径名扩展的影响。要从行构建数组,请考虑使用 mapfile (Bash ≥4) 作为:mapfile -t files &lt; &lt;(ls -1)。无需摆弄IFS。而且它也更短。
      • 当你有你的数组时,你可以使用IFS加入字段:saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS。或者,如果您想要一个包含多个字符的分隔符,请使用另一种方法。
      • @gniourf_gniourf:我的回答中包含了您的建议。谢谢。
      【解决方案6】:

      随便打

      mystring=$(printf "%s|" *)
      echo ${mystring%|}
      

      【讨论】:

      • 使用 "printf -v mystring "%s|" *" 会更有效一些 - 这样可以避免 $() 的分叉
      • 但值得注意的是,它不会对尾随的|、@camh 产生影响。
      • 好吧,只需 bash 和 gnu coreutils printf
      • @camh 但printf -v 仅适用于 bash,而提供的答案适用于许多 shell 类型。
      • @Christopher 是的,这将删除结尾的 |,前提是两行都使用:printf -v mystring "%s|" * ; echo ${mystring%|}
      【解决方案7】:

      设置IFS 和使用"$*" 的组合可以为所欲为。我正在使用一个子shell,所以我不会干扰这个shell的$IFS

      (set -- *; IFS=,; echo "$*")
      

      要捕获输出,

      output=$(set -- *; IFS=,; echo "$*")
      

      【讨论】:

      • 您是否有更多关于set 工作原理的信息?对我来说有点像巫毒教。对man set 的浅浅浏览也没有给我带来太多信息。
      • 如果你给set 一堆参数但没有选项,它会设置位置参数($1, $2, ...)。 -- 可以保护set,以防第一个参数(或本例中的文件名)碰巧以破折号开头。请参阅help set 中对-- 选项的描述。我发现位置参数是一种处理事物列表的便捷方式。我也可以用一个数组来实现这个:output=$( files=(*); IFS=,; echo "${files[*]}" )
      • 这很棒,因为它不需要执行任何额外的程序,而且它适用于包含空格甚至换行符的文件名。
      • @EhteshChoudhury 正如type set 会告诉你的那样,set is a shell builtin。所以,man set 无济于事,但help set 会。答案:“-- 将任何剩余的参数分配给位置参数。”
      • set -- * 之后。将* 的扩展延迟一层,您可以获得正确的输出,而无需子外壳:IFS=',' eval echo '"$*"'。当然这会改变位置参数。
      【解决方案8】:

      类似于第一个选项,但省略了尾随分隔符

      ls -1 | paste -sd "," -
      

      【讨论】:

      • 作为一个注释,我尝试的粘贴版本需要一个“-”最后的参数来告诉它从标准输入中读取。例如ls -1 | paste -s -d ":" -不确定这是否适用于所有版本的粘贴
      • 这个更好,因为它允许空分隔符:)
      • 注意paste 默认获取-(标准输入),至少在我的paste (GNU coreutils) 8.22 上。
      • 我刚刚投了赞成票,这是现在它与所选答案的票数相同。这就是答案。没有尾随分隔符
      • 可以使用"\0"指定空分隔符,所以paste -sd "\0" -对我有用!
      【解决方案9】:

      ls 连接到管道时会产生一列输出,因此-1 是多余的。

      这是另一个使用内置 join 函数的 perl 答案,它不会留下尾随分隔符:

      ls | perl -F'\n' -0777 -anE 'say join ",", @F'
      

      不起眼的-0777 让perl 在运行程序之前读取所有输入。

      不留下尾随分隔符的 sed 替代方案

      ls | sed '$!s/$/,/' | tr -d '\n'
      

      【讨论】:

        【解决方案10】:

        我觉得这个不错

        ls -1 | awk 'ORS=","'
        

        ORS 是“输出记录分隔符”,所以现在您的行将用逗号连接。

        【讨论】:

        • 这不排除尾随分隔符。
        • 这特别棒,因为它可以处理多字符记录分隔符(例如," OR "
        【解决方案11】:

        此命令适用于 PERL 爱好者:

        ls -1 | perl -l40pe0
        

        这里的 40 是空格的八进制 ascii 代码。

        -p 将逐行处理并打印

        -l 将负责用我们提供的 ascii 字符替换尾随的 \n。

        -e 是通知 PERL 我们正在执行命令行。

        0 表示实际上没有要执行的命令。

        perl -e0 等同于 perl -e ' '

        【讨论】:

          【解决方案12】:

          不要重新发明轮子。

          ls -m
          

          它就是这么做的。

          【讨论】:

          • OP 想要任何分隔符,所以你仍然需要一个 tr 来转换逗号。它还在逗号后添加一个空格,即 file1、file2、file3
          • 所以使用ls -mtr 删除逗号后的空格,你会这样做ls -m | tr -d ' '
          • 使用 tr 会删除文件名中的空格。最好使用sed 's/, /,/g
          【解决方案13】:

          ls 具有选项-m 以使用逗号和空格", " 分隔输出。

          ls -m | tr -d ' ' | tr ',' ';'
          

          将此结果传送到tr 以删除空格或逗号将允许您再次将结果传送到tr 以替换分隔符。

          在我的示例中,我将分隔符 , 替换为分隔符 ;

          ; 替换为您喜欢的任何一个字符分隔符,因为 tr 仅考虑您作为参数传入的字符串中的第一个字符。

          【讨论】:

            【解决方案14】:
            sed -e :a -e '/$/N; s/\n/\\n/; ta' [filename]
            

            解释:

            -e - 表示要执行的命令
            :a - 是一个标签
            /$/N - 定义当前行和 (N) 下一行的匹配范围
            @ 987654326@ - 将所有 EOL 替换为 \n
            ta; - 如果匹配成功则转到标签 a

            取自my blog

            【讨论】:

              【解决方案15】:

              一般解析lsis not advised,所以另一种更好的方法是使用find,例如:

              find . -type f -print0 | tr '\0' ','
              

              或者使用findpaste

              find . -type f | paste -d, -s
              

              对于一般连接多行(与文件系统无关),请检查:Concise and portable “join” on the Unix command-line

              【讨论】:

                【解决方案16】:

                您可以使用 chomp 将多行合并为单行:

                perl -e 'while () { if (/\$/ ) { chomp; } print ;}' bad0 >test

                if语句中加入换行条件,可以是特殊字符,也可以是任意分隔符。

                【讨论】:

                  【解决方案17】:

                  看起来答案已经存在。

                  如果你愿意 a, b, c 格式,使用ls -m (Tulains Córdova’s answer)

                  或者如果你想要a b c格式,使用ls | xargsChris J’s answer的简化版)

                  或者,如果您想要任何其他分隔符,例如 |,请使用 ls | paste -sd'|'Artem’s answer 的应用程序)

                  【讨论】:

                    【解决方案18】:

                    除了 majkinetor 的回答之外,这里是删除尾随分隔符的方法(因为我还不能在他的回答下发表评论):

                    ls -1 | awk 'ORS=","' | head -c -1
                    

                    只需删除与分隔符一样多的尾随字节即可。

                    我喜欢这种方法,因为我可以使用多字符分隔符 + awk 的其他好处:

                    ls -1 | awk 'ORS=", "' | head -c -2
                    

                    编辑

                    正如 Peter 所注意到的,本机 MacOS 版本的 head 不支持负字节数。然而,这很容易解决。

                    首先,安装coreutils。 “GNU Core Utilities 是 GNU 操作系统的基本文件、shell 和文本操作实用程序。”

                    brew install coreutils
                    

                    MacOS 提供的命令也使用前缀“g”安装。例如gls

                    完成此操作后,您可以使用具有负字节数的ghead,或者更好地使用别名:

                    alias head="ghead"
                    

                    【讨论】:

                    • 注意:负字节数仅在某些版本的 head 上支持,因此这不适用于例如宏。
                    • 感谢您指出这一点。我为 MacOS 添加了解决方法。
                    【解决方案19】:

                    sed 方式,

                    sed -e ':a; N; $!ba; s/\n/,/g'
                      # :a         # label called 'a'
                      # N          # append next line into Pattern Space (see info sed)
                      # $!ba       # if it's the last line ($) do not (!) jump to (b) label :a (a) - break loop
                      # s/\n/,/g   # any substitution you want
                    

                    注意

                    这在复杂性上是线性的,在所有行都附加到 sed 的模式空间之后只替换一次。

                    @AnandRajaseka 的 answer 和其他一些类似的答案,例如 here,是 O(n²),因为每次将新行附加到模式空间时 sed 都必须进行替换。

                    比较,

                    seq 1 100000 | sed ':a; N; $!ba; s/\n/,/g' | head -c 80
                      # linear, in less than 0.1s
                    seq 1 100000 | sed ':a; /$/N; s/\n/,/; ta' | head -c 80
                      # quadratic, hung
                    

                    【讨论】:

                      【解决方案20】:

                      带有斜杠处理的快速 Perl 版本:

                      ls -1 | perl -E 'say join ", ", map {chomp; $_} <>'
                      

                      解释一下:

                      • perl -E:执行带有特性支持的 Perl(比如,...)
                      • 说:打印带有承运人退货
                      • 加入 ", ", ARRAY_HERE:用 ", " 加入数组
                      • 地图 {chomp; $_} ROWS:从承运人返回的每一行中删除并返回结果
                      • :stdin,每一行都是一个ROW,加上一个map会创建一个每个ROW的数组

                      【讨论】:

                        【解决方案21】:

                        如果 Python3 是您的最爱,您可以这样做(但请解释一下为什么要这样做?):

                        ls -1 | python -c "import sys; print(','.join(sys.stdin.read().splitlines()))"
                        

                        【讨论】:

                        • 我不知道 OP 为什么要这样做,但我知道我为什么需要这样做:为了复制以空格分隔的所有文件名以将它们用作 rubocop 的参数, eslint、stylelint、haml-lint 等
                        【解决方案22】:

                        上面的 Python 答案很有趣,但自己的语言甚至可以使输出变得漂亮:

                        ls -1 | python -c "导入系统;打印(sys.stdin.read().splitlines())"

                        【讨论】:

                          猜你喜欢
                          • 2018-10-24
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-04-11
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多