使用 LS 命令列出目录时的文件类型和取消引用

Fel*_*eão 6 command-line ls shell symlink

我正在尝试将参数组合到ls命令中以列出目录内容。我基本上想要实现的是列出取消引用所有链接的目录,但明确显示列出的项目是一个链接。我尝试组合--dereference--classify选项,但没有显示链接符号 ( @),我得到了 a,*因为链接针对的是一个可执行文件。

关于如何获得这样的结果的任何想法?我对ls命令以外的其他选择持开放态度。

编辑: 我实际上使用ls命令中的其他选项。我当前的命令及其选项如下:

ls -ogq -LB --group-directories-first --time-style=long-iso
Run Code Online (Sandbox Code Playgroud)

我打算在我正在构建的应用程序中解析输出,并且不能使用带有箭头 ( ->)的默认链接输出。目录中的其他项目(例如文件夹和文件)也必须列出。

编辑(2):

澄清一下,我正在开发一个 Java 应用程序,它使用 SSH2 API 连接到服务器并列出目录。然后,使用 jsTree jQuery 插件将列表中的结果用于填充树。目前我上面引用的命令给了我以下输出:

felipe@simba:/mnt/drive$ ls -ogq --group-directories-first --time-style=long-iso 
total 12
drwxr-sr-x 2 4096 2014-06-11 18:04 folder1
drwxr-sr-x 6 4096 2014-06-27 19:35 folder2
dr-Sr-s-wt 2 4096 2014-06-27 13:51 folderWithPermissions
-rw-r--r-- 1    0 2014-06-30 10:42 file.txt
lrwxrwxrwx 1   49 2014-06-30 11:36 linkTeste -> folder2/dir/otherfile.txt
Run Code Online (Sandbox Code Playgroud)

通过应用正则表达式,我可以通过查看权限来识别文件夹、文件或链接是什么。但是当我有一个链接时,我只需要列出链接名称,而不是name -> destination. 如果我使用该命令的-L选项,ls则只输出链接名称,并且我获得了引用目标的权限(这是一件好事),因为该-L选项取消了链接的引用,但这样我就无法知道该链接实际上是一个关联。

felipe@simba:/mnt/drive$ ls -ogq -L --group-directories-first --time-style=long-iso 
total 12
drwxr-sr-x 2 4096 2014-06-11 18:04 folder1
drwxr-sr-x 6 4096 2014-06-27 19:35 folder2
dr-Sr-s-wt 2 4096 2014-06-27 13:51 folderWithPermissions
-rw-r--r-- 1    0 2014-06-30 10:42 file.txt
-rwxr-xr-x 1    0 2014-06-27 18:40 linkTeste
Run Code Online (Sandbox Code Playgroud)

我只需要列出链接名称,知道它是一个链接并知道目标是什么(文件、文件夹或链接)。我可以处理不同类型的输出,因为无论如何我都将应用正则表达式。

也欢迎使用find或替代stat

ter*_*don 5

编辑以回应更新的问题

由于您只关心链接、目录和常规文件,并且不需要处理ls可以识别的其他文件类型(FIFO、套接字等),因此您可以执行类似stat. 对于下面的示例,我创建了以下测试环境:

$ ls -l
total 4.0K
-rw-r--r-- 1 terdon terdon    0 Jun 30 23:12 a new?line 
-rw-r--r-- 1 terdon terdon    0 Jun 30 23:12 a space
-rw-r--r-- 1 terdon terdon    0 Jun 30 23:12 a?tab
drwxr-xr-x 2 terdon terdon 4.0K Jun 30 23:11 dir1
lrwxrwxrwx 1 terdon terdon    4 Jun 30 23:13 linktodir1 -> dir1
lrwxrwxrwx 1 terdon terdon    7 Jun 30 23:13 sh -> /bin/sh
Run Code Online (Sandbox Code Playgroud)

如您所见,这些包括链接、可执行文件的链接、带有空格的文件名、带有制表符 ( \t)的文件名和带有换行符 ( \n)的文件名。这些文件中的大多数会破坏您的ls方法,但stat可以正确处理它们:

$ stat --printf "%A\t%N\t%F\n" * 
-rw-r--r--  ‘a new\nline’   regular file
-rw-r--r--  ‘a space’   regular file
-rw-r--r--  ‘a\ttab’    regular file
drwxr-xr-x  ‘dir1’  directory
lrwxrwxrwx  ‘linktodir1’ -> ‘dir1’  symbolic link
lrwxrwxrwx  ‘sh’ -> ‘/bin/sh’   symbolic link
Run Code Online (Sandbox Code Playgroud)

的相关部分man stat

--printf=格式

类似于 --format,但解释反斜杠转义,并且不输出强制性的尾随换行符。如果您想要换行符,请在 FORMAT 中包含 \n

%A 人类可读形式的访问权限

%F 文件类型

%N 带引号的文件名,如果是符号链接,则取消引用

请注意,字段由 分隔\t,这意味着您将能够优雅地处理字段内的空格(例如文件名)。

你提到你无法处理->. 我不完全确定为什么,但你可以删除它sed

$ stat --printf "%A\t%N\t%F\n" * | sed 's/->//' 
lrwxrwxrwx  ‘linktodir1’  ‘dir1’    symbolic link
Run Code Online (Sandbox Code Playgroud)

或用另一个字符串替换它:

$ stat --printf "%A\t%N\t%F\n" * | sed 's/->/?/' | grep linktodir
lrwxrwxrwx  ‘linktodir1’ ? ‘dir1’   symbolic link
Run Code Online (Sandbox Code Playgroud)

或者只是解析文件类型。


根据您要执行的操作,将您要搜索的三种文件类型中的每一种分开并分别处理可能会很有用。如果是这样,请使用find1及其-printf选项:

$ find ./ -maxdepth 1 -mindepth 1 -type f -printf '%M\t%P\t%l\n'  ## files
$ find ./ -maxdepth 1 -mindepth 1 -type d -printf '%M\t%P\t%l\n'  ## directories
$ find ./ -maxdepth 1 -mindepth 1 -type l -printf '%M\t%P\t%l\n'  ## links
Run Code Online (Sandbox Code Playgroud)

在这种情况下,printf指令是

          %M     File's permissions (in symbolic form, as for  ls).   This
                 directive is supported in findutils 4.2.5 and later.
          %P     File's  name  with  the name of the command line argument
                 under which it was found removed.
          %l     Object of symbolic link (empty string if file  is  not  a
                 symbolic link).
Run Code Online (Sandbox Code Playgroud)

您还可以将上述内容组合成一个命令(使用find's-o运算符),但它允许您-printf根据文件类型打印任意字符串。例如:

$ find ./ -maxdepth 1 -mindepth 1 \( -type l -printf 'link:\t%M\t%P\t%l\n' \) \
-o \( -type d -printf 'dir:\t%M\t%P\n' \) \
-o \( -type f -printf 'file:\t%M\t%P\n' \) 
file:   -rw-r--r--  a?tab
file:   -rw-r--r--  a space
link:   lrwxrwxrwx  linktodir1  dir1
file:   -rw-r--r--  a new?line
dir:    drwxr-xr-x  dir1
link:   lrwxrwxrwx  sh  /bin/sh
Run Code Online (Sandbox Code Playgroud)

上面的命令将解释\t\n如果在终端上没有示出其正确地输出。但是,要正确处理带有换行符的文件名,您在解析时需要小心(确保“行”以 开头[file|dir|link]:)或\0在每次printf调用中用作行终止符而不是\n

$ find ./ -maxdepth 1 -mindepth 1 \( -type l -printf 'link:\t%M\t%P\t%l\0' \) \
-o \( -type d -printf 'dir:\t%M\t%P\0' \) \
-o \( -type f -printf 'file:\t%M\t%P\0' \)
Run Code Online (Sandbox Code Playgroud)

1 -maxdepth并且-mindepth是 GNU 扩展,因此这种方法仅适用于 GNU find


以下内容被发布为问题的第一个不太具体的版本的解决方案。我将它们留在这里,因为它们可能对其他人有用。

  1. 壳牌和 readlink

    for f in *; do 
     readlink "$f" >/dev/null && echo "$(readlink -f "$f") (link)" || echo "$f";
    done
    
    Run Code Online (Sandbox Code Playgroud)

    示例输出:

    /etc (link)
    foo
    sample.R
    sample.R~
    
    Run Code Online (Sandbox Code Playgroud)

    上面遍历当前下的所有文件和目录,如果readlink返回成功(如果$f是链接),它将取消引用它(readlink -f注意这将跟随所有链接。如果你只想要第一级,删除-f)并打印目标与(link). 如果不是,它只会打印$f

  2. 如果这仅适合您而不打算进行解析,请使用ls -l

    $ ls -l
    total 512116
    -rw-r--r-- 1 terdon terdon    100641 Jun 30 19:10 er
    lrwxrwxrwx 1 terdon terdon         5 Jun 30 19:12 etc -> /etc/
    -rw-r--r-- 1 terdon terdon 524288000 Jun 30 19:10 foo
    -rwxr--r-- 1 terdon terdon       353 Jun 30 15:22 sample.R
    -rwxr--r-- 1 terdon terdon       249 Jun 30 14:51 sample.R~
    
    Run Code Online (Sandbox Code Playgroud)

    这将清楚地表明与 的链接link -> target