Editar em resposta à pergunta atualizada
Como você só se preocupa com links, diretórios e arquivos regulares, e não precisa lidar com outros tipos de arquivos que ls
pode identificar (FIFOs, soquetes, etc), você poderia fazer algo como stat
. Para os exemplos abaixo, criei o seguinte ambiente de teste:
$ 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
Como você pode ver, estes incluem links, links para executáveis, um nome de arquivo com um espaço, um com uma guia ( \t
) e um com uma nova linha ( \n
). A maioria desses arquivos quebraria sua abordagem ls
, mas stat
pode lidar com eles corretamente:
$ 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
As seções relevantes de man stat
:
--printf=FORMAT
like --format, but interpret backslash escapes, and do not output a mandatory trailing newline. If you want a newline, include \n in FORMAT
%A access rights in human readable form
%F file type
%N quoted file name with dereference if symbolic link
Observe que os campos são separados por \t
, isso significa que você poderá lidar com espaços em branco dentro de campos (em nomes de arquivos, por exemplo) normalmente.
Você mencionou que não pode lidar com ->
. Eu não tenho certeza do porquê, mas você pode simplesmente remover isso com sed
$ stat --printf "%A\t%N\t%F\n" * | sed 's/->//'
lrwxrwxrwx ‘linktodir1’ ‘dir1’ symbolic link
ou substituí-lo por outra string:
$ stat --printf "%A\t%N\t%F\n" * | sed 's/->/→/' | grep linktodir
lrwxrwxrwx ‘linktodir1’ → ‘dir1’ symbolic link
ou apenas analise o tipo de arquivo.
Dependendo do que você deseja fazer, pode ser útil separar cada um dos três tipos de arquivos que você está procurando e lidar com cada um deles separadamente. Em caso afirmativo, use find
1 e sua opção -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
Nesse caso, as diretivas printf
são
%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).
Você também pode combinar o acima em um único comando (usando o operador find
-o
), mas permite usar -printf
para imprimir uma string arbitrária dependendo do tipo de arquivo. Por exemplo:
$ 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
O comando acima interpretará \t
e \n
corretamente se sua saída não for mostrada em um terminal. No entanto, para lidar com nomes de arquivos com novas linhas corretamente, você precisará ter cuidado ao analisar (certifique-se de que uma "linha" começa com [file|dir|link]:
) ou use
como terminador de linha em cada chamada printf
\n
em vez de -maxdepth
:
$ find ./ -maxdepth 1 -mindepth 1 \( -type l -printf 'link:\t%M\t%P\t%lfor f in *; do
readlink "$f" >/dev/null && echo "$(readlink -f "$f") (link)" || echo "$f";
done
' \) \
-o \( -type d -printf 'dir:\t%M\t%P/etc (link)
foo
sample.R
sample.R~
' \) \
-o \( -type f -printf 'file:\t%M\t%P$ 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~
' \)
1 -mindepth
e find
são extensões GNU, portanto, essa abordagem funcionará apenas para o GNU readlink
.
Os seguintes foram publicados como uma solução para a primeira versão menos específica da questão. Estou deixando-os aqui, pois podem ser úteis para os outros.
-
Shell e
readlink
$ 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
Exemplo de saída:
$ 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
O texto acima percorre todos os arquivos e diretórios sob o atual e se
$f
retornar bem-sucedido (sereadlink -f
for um link), ele será cancelado (-f
, observe que isso seguirá todos Se você deseja apenas o primeiro nível, remova o(link)
) e imprima o destino junto com$f
. Se não for, apenas imprimiráls -l
. -
Se isso é apenas para você e não pretende ser analisado, use apenas
link -> target
:$ stat --printf "%A\t%N\t%F\n" * | sed 's/->//' lrwxrwxrwx ‘linktodir1’ ‘dir1’ symbolic link
Isso indicará claramente os links com %code% .