A sabedoria aceita nas últimas décadas é que nunca é uma boa ideia analisar a saída de ls
( [1] , [2] ). Por exemplo, se eu quiser salvar a data de modificação de um arquivo junto com seu nome em uma variável shell, esta não é a maneira correta de fazer isso:
$ ls -l file
-rw-r--r-- 1 terdon terdon 0 Aug 15 19:16 file
$ foo=$(ls -l file | awk '{print $9,$6,$7,$8}')
$ echo "$foo"
file Aug 15 19:16
Assim que o nome do arquivo for ligeiramente diferente, a abordagem falha:
$ ls -l file*
-rw-r--r-- 1 terdon terdon 0 Aug 15 19:16 'file with spaces'
$ foo=$(ls -l file* | awk '{print $9,$6,$7,$8}')
$ echo "$foo"
file Aug 15 19:16
Fica pior se a data de modificação do arquivo não for próxima de hoje, pois isso pode alterar o formato da hora:
$ ls -l
total 0
-rw-r--r-- 1 terdon terdon 0 Aug 15 19:21 file
-rw-r--r-- 1 terdon terdon 0 Aug 15 2018 'file with spaces'
No entanto, as versões mais recentes do GNU coreutils ls
têm duas opções que podem ser combinadas para definir um formato de hora específico e produzir uma saída delimitada por NULL:
--time-style=TIME_STYLE
time/date format with -l; see TIME_STYLE below
[...]
--zero end each output line with NUL, not newline
[...]
The TIME_STYLE argument can be full-iso, long-iso, iso, locale, or
+FORMAT. FORMAT is interpreted like in date(1). If FORMAT is FOR‐
MAT1<newline>FORMAT2, then FORMAT1 applies to non-recent files and
FORMAT2 to recent files. TIME_STYLE prefixed with 'posix-' takes ef‐
fect only outside the POSIX locale. Also the TIME_STYLE environment
variable sets the default style to use.
Aqui estão os arquivos novamente, com essas opções definidas (o zero no final de cada linha de saída é substituído por #
e uma nova linha aqui para melhorar a legibilidade):
$ ls -l --zero --time-style=long-iso -- *
-rw-r--r--+ 1 terdon terdon 0 2023-08-16 21:35 a file with a
newline#
-rw-r--r--+ 1 terdon terdon 0 2023-08-15 19:16 file#
-rw-r--r--+ 1 terdon terdon 0 2018-08-15 12:00 file with spaces#
Com essas opções disponíveis, posso fazer muitas das coisas que ls
tradicionalmente são ruins. Por exemplo:
Obtenha o nome do arquivo modificado mais recentemente em uma variável:
$ touch 'a file with a'$'\n''newline' $ last=$(ls -tr --zero | tail -z -n1) bash: warning: command substitution: ignored null byte in input $ printf -- 'LAST: "%s"\n' "$last" LAST: "a file with a newline"
O exemplo que gerou essa pergunta. Outra pergunta, no Ask Ubuntu, onde o OP queria imprimir o nome do arquivo e a data de modificação. Alguém postou uma resposta usando
ls
e umawk
truque inteligente e, se somarmos--zero
als
, parece ser bem robusto:$ output=$(ls -l --zero --time-style=long-iso -- * | awk 'BEGIN{RS="\0"}{ t=index($0,$7); print substr($0,t+6), $6 }') $ printf 'Output: "%s"\n' "$output" Output: "a file with a newline 2023-08-16"
Não consigo encontrar um nome que quebre qualquer um desses dois exemplos. Então, minhas perguntas são:
- Existe um caso que falharia em um dos dois exemplos acima? Talvez alguma esquisitice local?
- Se não, isso significa que as versões modernas do GNU
ls
podem realmente ser usadas com segurança com nomes de arquivo arbitrários?