¿Cuándo la cadena vacía denota el directorio actual?

8

En un script que uso findpara recopilar algunos archivos en el directorio actual, como en

$ find . -name "*.h"
./foo.h

Ahora me gustaría que solo salga foo.h, sin el ./prefijo. Pensé que la cadena vacía ""denotaba el directorio actual en los comandos de shell. Pero esto da:

$ find "" -name "*.h"
find: ftsopen: No such file or directory

Entonces me equivoqué. Ahora mi pregunta es cuándo / cómo / dónde / .. ¿una "cadena vacía (?)" Denota el directorio actual en los comandos que esperan un nombre de archivo o un nombre de ruta? ¿Hay una explicación clara y esclarecedora?

Una pregunta secundaria es si el hallazgo de nitpicking anterior se puede resolver simplemente, sin manipulación de cadenas a la ${parameter#word}o cuto sed.

phs
fuente
1
findtiene el parámetro -printf para manipular cómo se muestran los resultados. find . -name '*.h' -printf '%P\n'eliminará el ./prefijo Mira lo que find . -name '*.h' -printhace.
Valentin Bajrami
¡Gracias! Lástima que mi versión de find no sea compatible con la -printfopción. FWIW, stringsen mi hallazgo da @(#)PROGRAM:find PROJECT:shell_cmds-175?!?
phs
55
@phs ese es el tipo de cosas que hace que sea esencial que siempre mencione su sistema operativo.
terdon

Respuestas:

11

Hace mucho tiempo (en la 7ª edición , 32V , 4.2BSD , 4.3BSD ), en el nivel de llamada del sistema, un nombre de ruta de longitud cero indicaba el directorio de trabajo actual (cuando se usaba para la búsqueda; no se permitía al intentar crear o eliminar un archivo o directorio). En el Sistema III , fue un error usar un nombre de ruta de longitud cero en todas las circunstancias, y el estándar POSIX tiene esto que decir sobre la resolución del nombre de ruta:

Un nombre de ruta nulo no se resolverá con éxito.

Mark Plotnick
fuente
3
Una cadena vacía en $ PATH, $ MANPATH, $ LD_LIBRARY_PATH (y otras, pero no necesariamente todas $ * PATH) denota el directorio actual. dir/es casi lo mismo quedir/.
Stéphane Chazelas
4

Puedes usar:

find * -name "*.h"

Tenga en cuenta que los archivos en el directorio actual cuyo nombre comience con .se omitirán, y los archivos cuyo nombre comience con -se interpretarán como opciones findy causarán estragos, por lo que esto no es un equivalente general de find . ….

La ausencia de una cadena que termine en " /" como parte de un nombre de archivo implica el directorio actual, pero eso no significa que el directorio actual se denota por una cadena vacía (que posiblemente no sea lo mismo que la ausencia de una cadena, aunque podría verse igual cuando se imprime).

Anthon
fuente
4

En general, la cadena vacía no denota el directorio actual, ni a los comandos de shell ni a las llamadas del sistema. Lo hizo en algunos sistemas más antiguos, pero no en los sistemas compatibles con POSIX .

Ocasionalmente encontrará un programa que usa el directorio actual cuando pasa una cadena vacía y el programa espera un nombre de directorio. Esto a veces es deliberado, y a veces es un efecto secundario de anteponer la ruta absoluta del directorio actual cuando la cadena dada no comienza con una barra inclinada.

Lo mejor para ti sería dejar el ./. No hace ningún daño.

Si la lista de archivos es para

find . … | sed 's!^\./!!'

Tenga en cuenta que esto altera algunos nombres de archivo que contienen nuevas líneas. Esto generalmente no es un problema para el consumo humano, y la producción de findno es adecuada para el consumo del programa, ya que es ambigua. Si está utilizando -print0, que es adecuado para el consumo del programa, probablemente no le importe el ./prefijo de todos modos.

Puede usar en find * …lugar de find . …, pero tenga en cuenta que find *tiene una serie de defectos que lo hacen inadecuado en general:

  • . se omite.
  • .Se omiten todos los archivos de puntos (archivos cuyo nombre comienza con o ..`).
  • Si hay un nombre de archivo en el directorio actual cuyo nombre comienza con -(o un archivo llamado !o (...), será interpretado como una opción o predicado por find.

El primer punto no importa si su filtro excluye el directorio actual. Para el segundo punto, puede usar los patrones ..?* .[!.]* *para hacer coincidir todos los archivos en el directorio actual, pero deberá verificar si cada patrón coincide con al menos un archivo y omitirlo si no lo hace. Esto es posible pero muy engorroso. El último punto es un tapón. Por find *lo tanto, puede ser adecuado para el uso rápido de la línea de comandos, pero no lo use en un script.

Un enfoque alternativo es utilizar la función de engrosamiento recursivo del shell, p. Ej.

printf '%s\n' **/*.h

Esto debe activarse shopt -s globstaren bash y set -o globstaren ksh93, y no existe en un shell POSIX básico como el guión. Los archivos de puntos no se atravesarán de forma predeterminada; para incluirlos, primero haga que el globbing no ignore los archivos de puntos shopt -s dotgloben bash o FIGNORE='@(.|..)'en ksh93. Además, si no hay coincidencias, este comando imprime el patrón; ejecuta shopt -s nullgloben bash para imprimir una línea vacía en su lugar, y usa el patrón ~(N)**/*.hen ksh.

En zsh, el bloqueo recursivo está activado de forma predeterminada. Use el calificador global Dpara incluir archivos de puntos e Nimprimir una línea vacía si no hay coincidencias (de manera predeterminada, zsh arroja un error si un patrón no coincide con ningún archivo). Puedes usar printfcomo arriba o

print -rl -- **/*.h(DN)
Gilles 'SO- deja de ser malvado'
fuente
Gracias por todos estos consejos. ¡La mayoría de los defectos find *son nuevos para mí, y algunos son aterradores!
phs
2

No puedo pensar en ningún ejemplo donde la cadena vacía denota el directorio actual .. Puede estar pensando en invocaciones como ls, pero eso se debe a que lsasume el directorio actual si no se proporciona ningún parámetro, y de hecho no tomará la cadena vacía:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory
Ulrich Schwarz
fuente
3
Un ejemplo donde la cadena vacía denota el directorio actual es como un PATHcomponente.
hvd
0

Hay varios trucos que puede usar para obtener la salida de find sin el líder ./:

  1. Use la -printfopción de búsqueda y dígale que solo imprima %f. Ver man find:

    % f Nombre del archivo con los directorios principales eliminados (solo el último elemento).

    Por ejemplo:

    find . -name "*.h" -printf "%f\n"
    
  2. Analiza la salida:

    find . -name "*.h" | sed 's#^./##'
    
  3. Truco de @ Anthon

    find * -name "*.h"
    

En cuanto a su otra pregunta, la cadena vacía nunca denota el directorio actual. Es solo que varios programas toman el directorio actual como predeterminado, por lo que cuando los ejecuta sin argumentos, se ejecutan en el directorio actual.

terdon
fuente
Eso supone GNU find. O %P./
úsalo
1
find * -name '*.h'encontrará foo/.bar.h, pero no .bar.hen el directorio actual.
Stéphane Chazelas
0

Si los archivos están en el directorio actual, puede usar ls:

$ ls *.sh

Si desea los archivos en subdirectorios también y solo necesita el nombre de archivo, puede hacer algo como:

$ find . -name '*.h' -exec basename {} \;

Hay comandos que obtendrán la ausencia de una cadena como el directorio actual, pero básicamente porque obtendrán el directorio actual por defecto si no se ingresa nada. El .siempre denota el directorio actual (y ..el directorio padre)

Karl
fuente
el OP solo quería ./ eliminado, mientras que su findsolución elimina todos los componentes del directorio
artm