【问题标题】:Bash - Regex to determine if output of ls -al is file or directory and hiddenBash - 正则表达式确定 ls -al 的输出是文件还是目录并隐藏
【发布时间】:2017-01-08 16:18:22
【问题描述】:

我正在尝试查找运行 ls -al 的每一行输出是否是一个文件或 目录以及是否隐藏并计算每个目录的类型。

编辑:我必须使用find

#!/bin/bash
#declare four different regex statements that match files, hidden files, directories and hidden directories (excluding . and ..)
#based on the output of each line of running ls -al
re_file='^\-[rwx\-]{9}\s[0-9]+\s([a-z_][a-z0-9_]{0,30})\s([a-z_][a-z0-9_]{0,30})\s[0-9]+\s\w{3}\s[0-9]+\s[0-9]{2}:[0-9]{2}\s[^\.](\w|\.)*$'
re_hidden_file='^\-[rwx\-]{9}\s[0-9]+\s([a-z_][a-z0-9_]{0,30})\s([a-z_][a-z0-9_]{0,30})\s[0-9]+\s\w{3}\s[0-9]+\s[0-9]{2}:[0-9]{2}\s\.\w(\w|\.)*$'
re_directory='^d[rwx\-]{9}\s[0-9]+\s([a-z_][a-z0-9_]{0,30})\s([a-z_][a-z0-9_]{0,30})\s[0-9]+\s\w{3}\s[0-9]+\s[0-9]{2}:[0-9]{2}\s[^\.](\w|\.)*$'
re_hidden_directory='^d[rwx\-]{9}\s[0-9]+\s([a-z_][a-z0-9_]{0,30})\s([a-z_][a-z0-9_]{0,30})\s[0-9]+\s\w{3}\s[0-9]+\s[0-9]{2}:[0-9]{2}\s\.\w(\w|\.)*$'
#declare four different counters for each type
file_count=0
hidden_file_count=0
directory_count=0
hidden_directory_count=0
#read through the output of ls -al line by line, assigning x the value of each line
ls -al $1 | while read x; do
  #test if each line matches each of the regex statements, if it does then increment the relevant counter
  if [[ $x =~ $re_file ]] ; then
    file_count+=1
  elif [[ $x =~ $re_hidden_file ]] ; then
    hidden_file_count+=1
  elif [[ $x =~ $re_directory ]] ; then
    directory_count+=1
  elif [[ $x =~ $re_hidden_directory ]] ; then
    hidden_directory_count+=1
  else
    echo "!!!"
  fi
done
total=$((file_count + hidden_file_count + directory_count + hidden_directory_count))
echo "Files found: $file_count (plus $hidden_file_count hidden)"
echo "Directories found: $directory_count (plus $hidden_directory_count hidden)"
echo "Total files and directories: $total"

目前,脚本输出!!!,因为与ls -al 的每一行的任何正则表达式语句都不匹配,并且所有计数器变量都保留在0。这是一个输入示例(尽管 Bash 在完成正则表达式检查之前删除了用于填充的额外空格)。

drwx--x--x  37 username groupname  4096 Jan  8 14:37 .
drwxr-xr-x 235 root     root       4096 Nov 15 12:16 ..
drwx------   3 username groupname  4096 Oct 27 14:35 .adobe
-rw-------   1 username groupname 14458 Dec  5 20:24 .bash_history
-rw-------   1 username groupname  2680 Sep 30 16:12 .bash_profile
-rw-------   1 username groupname  1210 Oct  7 09:40 .bashrc
drwx------  12 username groupname  4096 Dec  6 15:24 .cache
drwxr-xr-x  17 username groupname  4096 Jan  8 14:37 .config
drwx------   4 username groupname  4096 Dec  5 17:51 dir1
drwx------   2 username groupname  4096 Nov 23 12:26 dir2
...

我已经在online Regex checker 上测试了正则表达式,他们按照我的意愿进行评估。我认为这是一个特定于 Bash 的问题。任何帮助表示赞赏。

【问题讨论】:

标签: regex linux bash shell


【解决方案1】:

我花了一些时间,但成功了。

我的做法:避免解析ls -l的输出。特别是这里你不需要它。启用选项以便for 循环中的* 看到隐藏的对象并针对对象类型测试每个对象(使用shopt)。

另外:a+=1 并没有按照你的想法去做。它只是在字符串末尾附加1

#!/bin/bash
#declare four different regex statements that match files, hidden files, directories and hidden directories (excluding . and ..)
#based on the output of each line of running ls -al
re_hidden_file='^\..*'
#declare four different counters for each type
file_count=0
hidden_file_count=0
directory_count=0
hidden_directory_count=0

# enable hidden files/directories
shopt -s dotglob
#read through the output of ls -al line by line, assigning x the value of each line
for x in * ; do
  #test if each line matches each of the regex statements, if it does then increment the relevant counter
  if [ -d "$x" ] ; then
  if [[ "$x" =~ $re_hidden_file ]] ; then
    hidden_directory_count=$((hidden_directory_count+1))
  else
    directory_count=$((directory_count+1))
  fi
  else

  if [[ "$x" =~ $re_hidden_file ]] ; then
    hidden_file_count=$((hidden_file_count+1))
  else
    file_count=$((file_count+1))
   fi
   fi
done


total=$((file_count + hidden_file_count + directory_count + hidden_directory_count))
echo "Files found: $file_count (plus $hidden_file_count hidden)"
echo "Directories found: $directory_count (plus $hidden_directory_count hidden)"
echo "Total files and directories: $total"

【讨论】:

  • @Jean-François_Fabre 你能解释一下shopt -s dotglob这行吗?以前从未见过。
  • for 也会发出隐藏文件/目录。
  • 明确地说,它改变了* 的行为,而不是for
  • 它改变了在for循环中使用的通配行为
  • @RobMurray: a+=1 会按照你的想法去做,如果你首先将 $a 声明为整数:declare -i a
【解决方案2】:

您不应该解析ls 来获取文件。使用 find 代替 nul 终止或通配符。

问题在于ls 会为原本合法的文件名生成模棱两可的输出。考虑:

$ touch a$'\t'b
$ touch a$'\n'b
$ ls -l a*
-rw-r--r--  1 andrew  wheel  0 Jan  8 08:25 a?b
-rw-r--r--  1 andrew  wheel  0 Jan  8 08:26 a?b

\t\n 的不可打印字符被替换为 ? 并呈现来自 ls 的那些文件不明确。

尾随空格也会发生同样的情况:

$ touch "a b c   "
$ touch "a b c       "
$ ls -al a\ b*
-rw-r--r--  1 andrew  wheel  0 Jan  8 08:44 a b c   
-rw-r--r--  1 andrew  wheel  0 Jan  8 08:44 a b c   

现在考虑使用find

$ find . -name "a*" -maxdepth 1 -print0 | xargs -0 printf   "'%s'\n"
'./a    b'
'./a
b'
'./a b c   '
'./a b c      '

或者只是通配符:

$ for fn in a*; do printf "'%s'\n" "$fn"; done
'a  b'
'a
b'
'a b c   '
'a b c      '

如果您想获取总目录和总文件,包括隐藏文件和目录,只需将其添加到您的 glob 模式中:

file_count=0
hidden_file_count=0
regular_directory_count=0
hidden_directory_count=0

echo "=====regular files and directories:"
for fn in *; do 
    printf "'%s'\n" "$fn" 
    if [ -d "$fn" ]; then
        regular_directory_count=$((regular_directory_count+1))
    else
        file_count=$((file_count+1))
    fi      
done
echo "====hidden files and direcotries:"
for fn in .*; do 
    printf "'%s'\n" "$fn"; 
    if [ -d "$fn" ]; then
        hidden_directory_count=$((hidden_directory_count+1))
    else
        hidden_file_count=$((hidden_file_count+1))
    fi          
done

printf "Regular files: %s regular directories: %s\n" $file_count $regular_directory_count
printf "Hidden files:  %s hidden directories:  %s\n" $hidden_file_count $hidden_directory_count
tf=$((hidden_file_count+file_count))
td=$((hidden_directory_count+regular_directory_count))
printf "Total files:   %s total directories:   %s\n"  $tf $td

给定:

$ ls -la
total 0
drwxr-xr-x   9 andrew  wheel   306 Jan  8 11:07 .
drwxrwxrwt  92 root    wheel  3128 Jan  8 10:58 ..
drwxr-xr-x   2 andrew  wheel    68 Jan  8 11:07 .hidden dir
-rw-r--r--   1 andrew  wheel     0 Jan  8 11:26 .hidden file
-rw-r--r--   1 andrew  wheel     0 Jan  8 11:26 a?b
-rw-r--r--   1 andrew  wheel     0 Jan  8 11:26 a?b
-rw-r--r--   1 andrew  wheel     0 Jan  8 11:26 a b c   
-rw-r--r--   1 andrew  wheel     0 Jan  8 11:26 a b c       
drwxr-xr-x   2 andrew  wheel    68 Jan  8 11:07 regular dir

运行它,你会得到:

=====regular files and directories:
'a  b'
'a
b'
'a b c   '
'a b c       '
'regular dir'
====hidden files and direcotries:
'.'
'..'
'.hidden dir'
'.hidden file'
Regular files: 4 regular directories: 1
Hidden files:  1 hidden directories:  3
Total files:   5 total directories:   4

如果您想排除 ... 隐藏目录,您可以在使用 .* 全局模式之前设置 GLOBIGNORE=".:.."

【讨论】:

  • 不幸的是,对于这个练习,我不允许使用find,因为它太容易了-_-而且我不确定我是否完全理解你的答案:(
  • 他创建了两个ls 显示的同名文件。
  • @Cyrus:输出将通过管道传输(因此未连接到终端),因此两者的显示方式不同(但这当然会给 OP 带来惊喜)。
  • @Cyrus @dawg 好的,我明白了,但我们假设这不是问题。为什么我的正则表达式无法正确计算?当我在问题的代码中的while 循环中echo $x 时,它会以我期望的正则表达式语句的格式给出文本。
  • @RobMurray:脚本中的所有 4 个 re_* 正则表达式都至少包含此错误。因此,您会收到!!!
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-11
相关资源
最近更新 更多