Linux 在 shell 脚本中列出文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9940688/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:30:47  来源:igfitidea点击:

Listing files in shell script

linuxbashshell

提问by Akhil Thayyil

When i try the below code am getting all files whose filename starts with E

当我尝试下面的代码时,我正在获取文件名以 E 开头的所有文件

#!/bin/bash

data=$(ls -trh E*)
for entry in ${data}
do
  echo ${entry}
done

But if i try the below code , which get the wildcard from argument , i am getting only the first filename

但是如果我尝试下面的代码,它从参数中获取通配符,我只得到第一个文件名

#!/bin/bash

data=$(ls -trh )
for entry in ${data}
do
  echo ${entry}
done

Can anyone help me to solve this ..

谁能帮我解决这个..

When i gave quotes like this myscript.sh 'E*' it worked fine , is there any way to do this without giving quotes ?

当我给出这样的引号时 myscript.sh 'E*' 它工作正常,有没有办法在不给出引号的情况下做到这一点?

采纳答案by brice

This is a shell expansion problem.

这是一个shell扩展问题。

Your shell will interpret wildcard characters before passing then to your process. For example:

您的 shell 将在传递给您的进程之前解释通配符。例如:

script.sh

脚本文件

#!/bin/bash
echo   

Running the above with a wildcard:

使用通配符运行上述内容:

> ./script.sh E*
Eva   Eve   Evolve

If you want to pass an argument without shell interpreting it first, you will have to quote it:

如果你想在没有 shell 解释的情况下传递一个参数,你必须引用它:

> ./script.sh 'E*'
E*

Better Solution using find:

更好的解决方案使用find

What you're actually trying to do is get a list of all files and folders in a given directory, in reverse modification time order (oldest first). The output of lsis notoriously painful to parse. It is preferable to use the powerful and flexible findcommand instead.

您实际上想要做的是获取给定目录中所有文件和文件夹的列表,按反向修改时间顺序(最旧的在前)。的输出ls是众所周知的解析痛苦。最好使用强大而灵活的find命令。

This is a one liner:

这是一个单班轮:

>  find ./ -maxdepth 1 -printf "%A@ %f
> 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
> curl example.com/site?q=hello&name=bob
> echo 23/(7/98) | bc
" | sort -z -n | while read -d '' date line; do echo "$line"; done mozdebug colortry hooks-bare mustache_bug_demo githug java try.sh ./
" | sort -z -n | while read -d '' date line; do echo "$line"; done

Which is probably cryptic, but makes sense once explained.

这可能是神秘的,但一旦解释就有意义了。

  • Find all files in this directory without recursing find ./ -maxdepth 1
  • For each file, print out their last modified time in second -printf "%A@
  • and their filename, separated by null characters %f\0"
  • Sort the null-separated strings by last modified time (numerically) sort -z -n
  • For every null separated string assign the timestamp to 'date' and the rest of the line to 'line': while read -d '' date line
  • Print the line echo "$line"
  • 不递归地查找此目录中的所有文件 find ./ -maxdepth 1
  • 对于每个文件,以秒为单位打印出他们上次修改的时间 -printf "%A@
  • 和它们的文件名,用空字符分隔 %f\0"
  • 按最后修改时间(数字)对空分隔的字符串进行排序 sort -z -n
  • 对于每个空分隔的字符串,将时间戳分配给“日期”,将行的其余部分分配给“行”: while read -d '' date line
  • 打印行 echo "$line"

For example, in my directory:

例如,在我的目录中:

> curl "example.com/site?q=hello&name=bob"
> echo "23/(7/98)" | bc

If you don't want the ./result, just take it out of your final set.

如果您不想要./结果,只需将其从最终设置中取出即可。

updated:With Sorpigal's suggestion to handle filenames with newlines.

更新:Sorpigal 建议使用换行符处理文件名。

Further note on shell expansion

关于外壳扩展的进一步说明

This is the same with things like parenthesis and ampersands. For example, the following curl commands will not work as expected:

这与括号和与号等内容相同。例如,以下 curl 命令将无法按预期工作:

bash yourscript.sh 'E*'

As the ampersand and parentheses will be interpreted by the shell before they are passed to the process as arguments.

因为 & 号和括号在作为参数传递给进程之前将由 shell 解释。

For this to work correctly, you will have to quote the args:

为了使其正常工作,您必须引用参数:

./script "E*"

回答by Andreas Wong

Did you enclose your wildcard when calling your script?

调用脚本时是否附上了通配符?

#!/bin/bash
data=$(ls -trh "$@" | cat -E)
while [ ${#data} != 0 ]
do
  echo ${data%%$*}
  data=${data#*$}
done

Otherwise your script will get things that start with E in your current directory and $1 becomes exactly the first file, thus the behavior

否则,您的脚本将在当前目录中获取以 E 开头的内容,而 $1 恰好成为第一个文件,因此行为

回答by Sandeep Pathak

This sound like a Shell Expansion problem . If you want to pass wild card to a shell , just quote it . eg .

这听起来像壳扩展问题。如果您想将通配符传递给 shell,只需引用它。例如。

#!/usr/bin/env bash
pat=""
while IFS= read -r -d '' file ; do
    echo "$file"
done < <(find . -maxdepth 1 -name "$pat" -print0)

Quoting would avoid shell expansion .

引用将避免 shell 扩展。

回答by pizza

Try this.

尝试这个。

myscript.sh E\*

回答by sorpigal

You should not be using lsin a shell script. The output of lsis for human consumption onlyand should never be used as the input to another command.

您不应该ls在 shell 脚本中使用。的输出ls是用于人类消费不应该被用作输入到另一个命令。

First, the correct solution to your problem.

首先,正确解决您的问题。

#!/bin/bash

data=("$(ls -trh "$@")")
for entry in "${data[@]}"
do
  echo "${entry}"
done

This presumes that you don't have any special ordering or name format requirements. You can refer to this answerif you need ordering like ls -t.

这假定您没有任何特殊的排序或名称格式要求。您可以参考这个答案,如果你需要订购类似ls -t

Your problem is common and I call it the "Who sees what?" problem. You say myscript.sh E*and expect a literal E*to be passed to your script, but in fact since this argument is unquoted the globwill be expanded by bashbefore your script is called, so what your script sees is all files in the current directory whose names started with E.

你的问题很常见,我称之为“谁看到了什么?” 问题。您说myscript.sh E*并期望将文字E*传递给您的脚本,但实际上,由于此参数未加引号,因此在调用脚本之前,glob将被扩展bash,因此您的脚本看到的是当前目录中名称以E.

As you note, single-quoting the argument "fixes" this. This is because bashdoesn't perform any special expansion inside single quotes, so now your script sees E*literally. You have, effectively, escaped the *so bashwon't expand it before passing it to your script. To do this without using quotes is possible by backslash-escape the *

正如您所注意到的,单引号参数“修复”了这一点。这是因为bash不会在单引号内执行任何特殊扩展,所以现在您的脚本可以看到E*字面意思。你已经有效地逃脱了,*所以在bash将它传递给你的脚本之前不会扩展它。要做到这一点而不使用引号可以通过反斜杠转义*

myscript.sh E*

But a superior solution is to allow bashto expand the glob and change your script to handle processing multiple file arguments instead. For example:

但是一个更好的解决方案是允许bash扩展 glob 并更改您的脚本以处理多个文件参数。例如:

"find-files.sh"
#!/bin/bash
store=
date_given=
inc_files=()
limit=$( date +"%s" -d "$date_given" )
while read -r -d $'
./find-files.sh '/a/dir/to/search' '2013-01-12 18:12:09'
' 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##代码##" | sort -zn)

Here I assume that all arguments will be file names. I have changed datainto an array and added quotes to all expansions, to preserve whitespace properly. This solution is still wrong, because it parses lsoutput, but now you probably get the behavior you expect out of the unquoted E*

这里我假设所有参数都是文件名。我已更改data为一个数组并为所有扩展添加了引号,以正确保留空格。此解决方案仍然是错误的,因为它解析ls输出,但现在您可能会从未引用的内容中获得您期望的行为E*

##代码##

In general you should alwaysquote the expansion of variables in shell scripts, because that probably does what you expect (and unquoted expansion probably does not!)

通常,您应该始终引用 shell 脚本中变量的扩展名,因为这可能符合您的预期(而未引用的扩展名可能不会!)

回答by centurian

Here's a quick example:

这是一个快速示例:

find files of a directory ordered by their modification dates from older to newer but less than or equal a given date and containing a '.inc' in their names

查找按修改日期从旧到新但小于或等于给定日期并在其名称中包含“.inc”的目录中的文件

##代码##

and call it:

并称之为:

##代码##

I agree with @Sorpigal https://unix.stackexchange.com/questions/22674/shell-script-for-moving-oldest-files/29205#29205it's very helpful except 'IFS=' in while loop that breaks everything in my system (Ubuntu).

我同意@Sorpigal https://unix.stackexchange.com/questions/22674/shell-script-for-moving-oldest-files/29205#29205它非常有用,除了while循环中的'IFS ='打破了我的一切系统(Ubuntu)。