如何使用自定义分隔符将多行文件名合并为一行

549

我该如何将 ls -1 的结果合并成一行,并使用任意分隔符delimit它?

22个回答

872

paste -s -d 命令可以使用分隔符(例如",")将多行文本合并为一行,并且最后不会留下分隔符:

ls -1 | paste -sd "," -

36
请注意,我尝试过的paste版本需要在末尾加上一个“-”参数才能告诉它从标准输入中读取。例如:ls -1 | paste -s -d ":" - 不确定这是否适用于所有版本的paste。 - Andy White
5
这个更好,因为它允许使用空分隔符 :) - iururu
2
注意,在我的 paste (GNU coreutils) 8.22 中,paste 命令默认使用 -(标准输入)。 - fedorqui
2
我刚刚点赞了它,现在它的票数与被选答案相同。 这就是答案。没有尾部分隔符。 - thebugfinder
1
空分隔符可以使用 "\0" 指定,所以对我来说 paste -sd "\0" - 是可行的! - Brad Parks
显示剩余4条评论

417

编辑:如果你想要逗号作为定界符,只需使用“ls -m”即可。

啊,威力和简洁!

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

将逗号 "," 更改为您想要的任何字符。请注意,这包括一个“尾随逗号”(用于以换行符结尾的列表)


51
+1,但更详细的版本应该以不同的方式处理最后的 \n。 - mouviciel
6
如果文件名中包含\n,这也会被替换掉。 - codaddict
3
他的意思是不要添加末尾的“,”,我相信。就像这样:ls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/' - Chris
7
@Chris:你的 sed 命令可以更加高效,使用结束标记字符: ls -1 | tr "\\n" "," | sed 's/,$//'; echo '' - pieman72
7
使用tr之后再使用sed似乎只是为了删除最后一个字���,这种做法似乎不太合理。我会选择使用ls -1 | tr '\n' ',' | head -c -1 - reddot
显示剩余8条评论

39

这将把最后一个逗号替换为换行符:

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的建议。

1
@dimir:这个问题的许多答案都存在这个问题。我已经编辑了我的答案,允许文件名包含制表符或空格,但不包括换行符。 - Dennis Williamson
你的Bash版本也受到路径名扩展的影响。如果要从行中构建数组,请考虑使用mapfile(Bash≥4),如下所示:mapfile -t files < <(ls -1)。无需调整IFS,而且更短。 - gniourf_gniourf
1
@gniourf_gniourf:我已经在我的答案中包含了你的建议。谢谢。 - Dennis Williamson
一个简单的 files=(*) 就可以正常工作。此外,对 * 的延迟评估将避免与 IFS 打交道:IFS=, command eval 'list=${files[*]}'; echo "<$list>"。当然,IFS=, command eval echo '"${files[*]}"' 更短,但不会设置 $list 变量字符串。 - user8017719
当然,* 的扩展仅适用于一个字符。 - user8017719
显示剩余3条评论

34

我认为这个很棒。

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

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


13
不包括末尾分隔符。 - Derek Mahar
7
这特别棒的原因在于它能够处理多字符记录分隔符(例如:" OR ")。 - Mat Schaffer

19

虽然如此,其他示例都是通用的,适用于你想要合并成一行的任何列表。 - undefined

15
设置 IFS 并使用 "$*" 的组合可以实现您想要的功能。我正在使用子shell,以免干扰当前shell中的 $IFS。
(set -- *; IFS=,; echo "$*")
为捕获输出,
output=$(set -- *; IFS=,; echo "$*")

2
你有关于set如何工作的更多信息吗?对我来说看起来有点像巫术。浅显地查看man set也没有给我带来太多信息。 - Ehtesh Choudhury
3
如果您向 set 命令提供了一系列参数但没有选项,它会设置位置参数($1, $2, ...)。-- 选项用于保护 set 命令,以防第一个参数(或在此情况下的文件名)恰好以破折号开头。请参阅 help set-- 选项的描述。我发现位置参数是处理事物列表的方便方法。我也可以使用数组来实现这个功能:output=$( files=(*); IFS=,; echo "${files[*]}" ) - glenn jackman
这很棒,因为它不需要执行任何额外的程序,并且可以处理包含空格甚至换行符的文件名。 - Eric
1
@EhteshChoudhury,正如“type set”所示,“set是一个shell内置命令”。因此,“man set”无法帮助,但“help set”可以。答案是:“--将任何剩余的参数分配给位置参数。” - Stéphane Gourichon
在执行 set -- * 后,通过延迟展开 * 一级,您可以获得正确的输出而无需使用子 shell:IFS=',' eval echo '"$*"'。当然,这将更改位置参数。 - user8017719

12

在 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"

1
注意:负字节计数仅在某些版本的head上受支持,因此在例如macOS上无法使用。 - Peter
谢谢指出这个问题。我已经为MacOS添加了一个解决方法。 - Aleksander Stelmaczonek

9
不要重复造轮子。
ls -m

它确实做到了这一点。

OP想要任何分隔符,所以您仍然需要使用tr来转换逗号。它还在逗号后添加了一个空格,即file1,file2,file3。 - rob
因此,使用 ls -mtr 命令来删除逗号后面的空格,您可以执行 ls -m | tr -d ' ' - Andrew
2
使用tr命令将删除文件名中的空格。最好使用sed 's/, /,/g' - glenn jackman

8
这个命令适合PERL爱好者使用:
ls -1 | perl -l40pe0

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

-p选项会逐行处理并打印输出。

-l选项会将每行末尾的\n替换为我们指定的ASCII字符。

-e选项告诉PERL我们正在进行命令行执行。

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

perl -e0和perl -e ' '是等效的。


8

只是Bash

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

5
更高效的方法是使用“printf -v mystring "%s|" *”,这可以避免使用 $() 产生新的进程。 - camh
但是需要注意的是,@camh,它不会去掉尾随的 | - Christopher
1
仅仅是 bash 和 gnu coreutils 的 printf - ThorSummoner
@camh 但是 printf -v 只能在 bash 中工作,而所示答案适用于许多 shell 类型。 - user8017719
引用回显变量${mystring%|}似乎是个好主意。 - user8017719
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接