当我尝试以下代码时,我将获取文件名以E
开头的所有文件#!/bin/bash
data=$(ls -trh E*)
for entry in ${data}
do
echo ${entry}
done
但是,如果我尝试下面的代码,从参数得到通配符,我只得到 第一个文件名
#!/bin/bash
data=$(ls -trh $1)
for entry in ${data}
do
echo ${entry}
done
任何人都可以帮我解决这个问题。
当我给出类似myscript.sh'E *'的引号时,它工作正常,有没有办法在没有引号的情况下这样做?
答案 0 :(得分:7)
在传递给您的进程之前,您的shell将解释通配符。例如:
<强> script.sh 强>
#!/bin/bash
echo $1 $2 $3
使用通配符运行上述内容:
> ./script.sh E*
Eva Eve Evolve
如果你想在不首先解释它的情况下传递参数,你必须引用它:
> ./script.sh 'E*'
E*
find
更好的解决方案:您实际要做的是获取给定目录中所有文件和文件夹的列表,反向修改时间顺序(最早的第一个)。 ls
的输出是非常痛苦的解析。最好使用功能强大且灵活的find
命令。
这是一个班轮:
> find ./ -maxdepth 1 -printf "%A@ %f\0" | sort -z -n | while read -d '' date line; do echo "$line"; done
这可能是神秘的,但一旦解释就有意义。
find ./ -maxdepth 1
-printf "%A@
%f\0"
sort -z -n
while read -d '' date line
echo "$line"
例如,在我的目录中:
> ls -l
total 4296
drwxr-xr-x 2 bfer cvs 4096 2012-03-05 15:49 colortry
drwxr-xr-x 3 bfer cvs 4096 2012-03-27 15:05 githug
drwxr-xr-x 3 bfer cvs 4096 2012-03-12 17:18 hooks-bare
drwxr-xr-x 3 bfer cvs 4096 2012-03-28 12:38 java
-rw-r--r-- 1 bfer cvs 4025413 2012-03-27 12:53 mozdebug
drwxr-xr-x 2 bfer cvs 4096 2012-02-16 12:54 mustache_bug_demo
-rwxr-xr-x 1 bfer cvs 113 2012-03-30 12:20 try.sh
> find ./ -maxdepth 1 -printf "%A@ %f\0" | sort -z -n | while read -d '' date line; do echo "$line"; done
mozdebug
colortry
hooks-bare
mustache_bug_demo
githug
java
try.sh
./
如果您不想要./
结果,请将其从最终设置中删除。
已更新:Sorpigal建议使用换行符处理文件名。
这与括号和&符号相同。例如,以下curl命令将无法按预期工作:
> curl example.com/site?q=hello&name=bob
> echo 23/(7/98) | bc
因为&符号和括号将在shell作为参数传递给进程之前由shell解释。
为了使其正常工作,您必须引用args:
> curl "example.com/site?q=hello&name=bob"
> echo "23/(7/98)" | bc
答案 1 :(得分:2)
在调用脚本时,您是否附上了通配符?
bash yourscript.sh 'E*'
否则,您的脚本将在当前目录中获取以E开头的内容,而$ 1将成为第一个文件,因此行为
答案 2 :(得分:2)
这听起来像是Shell扩展问题。如果你想将通配符传递给shell,只需引用它即可。例如。
./script "E*"
引用会避免shell扩展。
答案 3 :(得分:1)
您不应该在shell脚本中使用ls
。 ls
的输出仅供人类消费 ,绝不能用作另一个命令的输入。
首先,正确解决您的问题。
#!/usr/bin/env bash
pat="$1"
while IFS= read -r -d '' file ; do
echo "$file"
done < <(find . -maxdepth 1 -name "$pat" -print0)
这假设您没有任何特殊的订购或名称格式要求。如果您需要ls -t
等订单,可以参考this answer。
你的问题很常见,我称之为&#34;谁看到了什么?&#34;问题。您说myscript.sh E*
并希望将文字E*
传递给您的脚本,但实际上由于此参数未加引号,glob将在您的脚本之前由bash
展开调用,所以脚本看到的是当前目录中名称以E
开头的所有文件。
正如您所说,单引号参数&#34;修复&#34;这个。这是因为bash
并未在单引号内执行任何特殊扩展,因此现在您的脚本会逐字地看到E*
。实际上,您已经转义了*
,因此bash
在将其传递到您的脚本之前不会展开它。要在不使用引号的情况下执行此操作,可以通过反斜杠转义*
myscript.sh E\*
但更好的解决方案是允许bash
扩展glob并更改脚本以处理多个文件参数。例如:
#!/bin/bash
data=("$(ls -trh "$@")")
for entry in "${data[@]}"
do
echo "${entry}"
done
这里我假设所有参数都是文件名。我已将data
更改为数组,并为所有扩展添加了引号,以便正确保留空格。这个解决方案仍然是错误的,因为它会解析ls
输出,但现在你可能会从不带引号的E*
myscript.sh E*
一般来说,你应该始终引用shell脚本中变量的扩展,因为这可能符合你的预期(而且不带引号的扩展可能不!)
答案 4 :(得分:0)
这是一个简单的例子:
查找按修改日期排序的目录文件,从较旧到较新但小于或等于给定日期,并在其名称中包含“.inc”
"find-files.sh"
#!/bin/bash
store=$1
date_given=$2
inc_files=()
limit=$( date +"%s" -d "$date_given" )
while read -r -d $'\0' seconds file; do
seconds=${seconds%.*}
if [[ $seconds -le $limit ]]; then
printf "seconds=$seconds, file=$file\n"
#block word splitting
inc_files=( "${inc_files[@]}" "$file" )
#do whatever you want here...
fi
done < <(find $store -maxdepth 1 -type f -name "*.inc*" -printf "%T@ %f\0" | sort -zn)
并称之为:
./find-files.sh '/a/dir/to/search' '2013-01-12 18:12:09'
我同意@Sorpigal https://unix.stackexchange.com/questions/22674/shell-script-for-moving-oldest-files/29205#29205它非常有用,除了'IFS ='在while循环中破坏了我系统中的所有内容(Ubuntu)。
答案 5 :(得分:-3)
试试这个。
#!/bin/bash
data=$(ls -trh "$@" | cat -E)
while [ ${#data} != 0 ]
do
echo ${data%%\$*}
data=${data#*\$}
done