我想做这样的事情,但它不会在管道结束后保存变量:
fs=( )
echo ${fs[@]}
ls -A1 |
while read f
do
echo ${fs[@]}
fs+=( "$f" )
echo ${fs[@]}
done
echo "All files/dirs: "${fs[@]}
对于当前目录中的文件 1、2 和 3,我得到以下输出:
# Output:
1
1
1 2
1 2
1 2 3
All files/dirs:
管道结束后如何保留 fs 变量的值?
更新:
- 无法使用 *,因为我需要隐藏文件
- 不能只使用 fs=($(ls)),而有时文件/目录名称中会有空格
答案1
在 shell 中列出目录中所有文件的方法很简单*
。
for f in *; do …
在支持数组的 shell(bash、ksh、zsh)中,您可以直接将文件列表分配给数组变量:fs=(*)
这会忽略点文件,这不利于您使用ls -A
.在 bash 中,dotglob
首先设置选项以包含点文件,nullglob
如果当前目录为空,则设置为空数组:
shopt -s dotglob nullglob
fs=(*)
在 ksh 中,使用FIGNORE=".?(.)"; fs=(~(N)*)
.在 zsh 中,使用D
和N
glob 限定符:fs=(*(DN))
。在其他 shell 中,这更加困难;最好的选择是包含每个模式*
(非点文件)、.[!.]*
(单点文件,不包括.
和双点文件)和..?*
(双点文件,不包括..
其本身),并检查每个模式是否为空。
set -- *; [ -e "$1" ] || shift
set .[!.]* "$@"; [ -e "$1" ] || shift
set ..?* "$@"; [ -e "$1" ] || shift
for x; do … # short for for x in "$@"; do …
我最好也解释一下你的尝试出了什么问题。主要问题是管道的每一端都在子进程中运行,因此fs
循环中的分配发生在子进程中,并且从未传递到父进程。 (大多数 shell 都是这种情况,包括 bash;两个例外是 ATT ksh 和 zsh,其中管道的右侧在父 shell 中运行。)您可以通过启动外部子进程并对其进行安排来观察这一点打印其父进程 ID1´2:
sh -c 'echo parent: $PPID'
{ sh -c 'echo left: $PPID >/dev/tty'; echo $? >/dev/null; } |
{ sh -c 'echo right: $PPID >/dev/tty'; echo $? >/dev/null; }
此外,您的代码还有两个可靠性问题:
- 不要解析 的输出
ls
。 read
破坏空格和反斜杠;要解析行,您需要while IFS= read -r line; do …
当您确实需要解析行并使用结果时,请将整个数据处理放在一个块中。
producer … | {
while IFS= read -r line; do
…
done
consumer
}
1
请注意,它$$
不会显示任何内容:它是主 shell 进程的进程 ID,在子 shell 中不会更改。
²
在某些 bash 版本中,如果您只是sh
在管道的一侧调用,您可能会看到相同的进程 ID,因为 bash 优化了对外部进程的调用。大括号的绒毛并echo $?
击败了这种优化。
答案2
这段代码将满足您的需求 -
#!/bin/sh
echo *
你到底想在这里做什么?
你也可以做
var=*
echo $var
如果你需要更多,我建议你去 stackoverflow。
答案3
你真的需要管子吗?您可以轻松地做到这一点:
fs=( )
echo ${fs[@]}
for file in `ls`
do
echo $file
fs+=$file
done
echo $fs
echo "All files/dirs: "${fs[@]}