GNU encuentra y enmascara el {} para algunos shells, ¿cuál?

34

La página del manual para GNU find states:

-exec command ;
    [...] The  string `{}'  is  replaced  by the current 
    file name being processed everywhere it occurs in the 
    arguments to the command, not just in arguments where 
    it is alone, as in some  versions  of  find.
    Both  of  these  constructions might need to be escaped 
    (with a `\') or quoted to protect them from expansion 
    by the shell. 

Eso es del hombre a find(GNU findutils) 4.4.2.

Ahora probé esto con bash y dash, y no es necesario que ambos {}estén enmascarados. Aquí hay una prueba simple:

find /etc -name "hosts" -exec md5sum {} \; 

¿Hay un caparazón, para el cual realmente necesito enmascarar los frenos? Tenga en cuenta que no depende de si el archivo encontrado contiene un espacio en blanco (invocado desde bash):

find ~ -maxdepth 1 -type d -name "U*" -exec ls -d {} \; 
/home/stefan/Ubuntu One

Esto cambia si el archivo encontrado se pasa a una subshell:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d {}' \; 
ls: cannot access /home/stefan/Ubuntu: No such file or directory
ls: cannot access One: No such file or directory

que puede resolverse mediante:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "$0"' {} \;

en contraste con:

find ~ -maxdepth 3 -type d -name "U*" -exec bash -c 'ls -d "{}"' \; 
/home/stefan/Ubuntu One

pero eso no es de lo que habla la página man, ¿verdad? Entonces, ¿qué shell trata {}de una manera diferente?

usuario desconocido
fuente
@Volker Siegel: su edición podría haber tenido buena intención, pero revisé mi manual de búsqueda actual y sus correcciones son incorrectas. También perdió la oportunidad de resumir sus ediciones, lo cual es un mal hábito, así que revierto sus cambios. Lo siento amigo.
usuario desconocido
Oh, gracias por revertirlo si rompió algo. Estaba recordando que el compilador de texto que genera el formato de página de manual utilizado para generar estas "comillas tipográficas" como un artefacto de la definición de representación. También puede renderizar a TeX para un formato de impresión perfecto. Noté las citas porque confundían el resaltado de sintaxis. Supuse que la primera cita inusual es incorrecta cuando se usa en código, y todavía asumo eso. Tenga en cuenta que los dos cambios fueron en el texto descriptivo, no en el código. Entonces, si los vemos como tipografía, representación estética, tienen razón.
Volker Siegel
Parece que en el renderizado de salida de información, se usa la misma cita en ambos lados. No hay duda de que mi cambio lo hizo diferente del original, tienes razón. ¿Pero ha causado algún problema? (Considero que la primera cita es un error en la definición de representación de hombre, es el único caso de tipografía hasta donde yo sé.)
Volker Siegel
Acabo de comprobar: `{} 'no funciona en la línea de comando. Eso es técnicamente correcto, pero no me gusta que un usuario desprevenido reciba un error extraño cuando intenta copiarlo y pegarlo.
Volker Siegel
@VolkerSiegel: Es texto en prosa, no código, entonces, ¿qué debe copiar y pegar un usuario aquí? Y es una cita de la página de manual de 2011. No veo ningún valor que corrija el pasaje para luego señalar, por qué hice correcciones a la página de manual. El tema es bastante complicado. Si esta fuera su pregunta, no trataría de convencerlo, de citar la página del manual con precisión, pero para mi pregunta, no estoy de humor para hacer compromisos. La cita es cita.
usuario desconocido

Respuestas:

26

Resumen : si alguna vez hubo un shell que se expandió {}, es un legado realmente antiguo por ahora.

En el shell Bourne y en los shells compatibles con POSIX, los corchetes ( {y }) son caracteres ordinarios (a diferencia de (y )que son delimitadores de palabras como ;y &, y [y ]que son caracteres globales). Se supone que las siguientes cadenas deben imprimirse literalmente:

$ echo { } {} {foo,bar} {1..3}
{ } {} {foo,bar} {1..3}

Una palabra que consiste en una sola llave es una palabra reservada , que solo es especial si es la primera palabra de un comando.

Ksh implementa la expansión de llaves como una extensión incompatible al shell Bourne. Esto se puede desactivar con set +B. Bash emula ksh a este respecto. Zsh también implementa la expansión de llaves; allí se puede apagar con set +Io setopt ignore_braceso emulate sh. Ninguno de estos shells se expande {}en ningún caso, incluso cuando es una subcadena de una palabra (p foo{}bar. Ej. ), Debido al uso común en argumentos para findy xargs.

Single Unix v2 señala que

En algunos sistemas históricos, las llaves se tratan como operadores de control. Para ayudar en futuras actividades de estandarización, las aplicaciones portátiles deben evitar el uso de llaves sin comillas para representar los propios caracteres. Es posible que una versión futura del estándar ISO / IEC 9945-2: 1993 lo requiera {y }sea ​​tratado individualmente como operadores de control, aunque el token {}probablemente será una excepción de caso especial debido a la find {}construcción de uso frecuente .

Esta nota se eliminó en versiones posteriores del estándar; los ejemplos parafind tienen usos sin comillas de {}, al igual que los ejemplos paraxargs . Puede haber habido conchas históricas de Bourne donde {}debían citarse, pero ahora serían sistemas heredados realmente antiguos.

Las implementaciones de CSH que tengo a la mano (OpenBSD 4.7, CSH BSD en Debian , tcsh) todo se expanden {foo}a foopero dejan {}solo.

Gilles 'SO- deja de ser malvado'
fuente
1
@geekosaur: solo un pequeño subconjunto de rebajas funciona en los comentarios . No sé lo que intentas decir. Si se trata de la expansión de llaves de ksh et al, lea mi párrafo que comienza con "Ksh implementa la expansión de llaves como una extensión incompatible".
Gilles 'SO- deja de ser malvado'
2
Eso fue bash(ver $BASH_VERSION). La expansión de llaves está muy viva y bien.
geekosaur
2
Ese era el punto. La {}sintaxis se originó en csh, pero se {}expandió a la cadena vacía. Los proyectiles más nuevos reconocen que eso no tiene sentido, pero todavía hay algunos viejos csh.
geekosaur
1
@geekosaur: ¿Puedes decir qué versión csh específica en qué plataforma específica? Preferiría probarlo yo mismo (si es posible en Linux) o estar seguro de que la persona lo prueba por sí mismo.
usuario desconocido
1
@geekosaur, {}foose expandiría a foo, pero {}se expandiría a {}(excepto cuando esté dentro de los backticks, pero las citas no ayudarían) y se documentó como tal. Lo verifiqué para el csh de 2BSD (primer lanzamiento), 2.79BSD, 2.8BSD y 2.11BSD.
Stéphane Chazelas
12

El {}necesitaba ser citado en versiones de la fishcáscara antes de 3.0.0.

$ fish -c 'echo find -exec {} \;'
find -exec  ;

Y en el shell rc (también akangabasado en rc, pero no es):

$ rc -c "echo find -exec {} ';'"
line 1: syntax error near '{'

Es probable que esos no sean los shells que los autores de esa documentación de GNU tenían en mente cuando escribieron ese texto desde que fishse lanzó por primera vez en 2005 (mientras que ese texto o similar ya estaba allí en 1994) y rcoriginalmente no era un shell de Unix.

Hay algunos rumores de que algunas versiones de csh(el shell que introdujo la expansión de llaves) lo requieren. Pero es difícil darles crédito a ellos ya que el primer lanzamiento de csh2BSD no lo hizo. Aquí, como se probó en un emulador PDP11:

# echo find -exec {} \;
find -exec {} ;

Y la página del manual de 2BSD cshdice claramente :

Como caso especial, `{',`}' y `{} 'se pasan sin molestias.

Por lo tanto, me resultaría muy extraño si una versión posterior de csh o tcsh lo rompiera más tarde.

Podría haber sido evitar algunos errores en algunas versiones. Aún con ese 2BSD csh (eso es lo mismo en 2.79BSD, 2.8BSD, 2.11BSD):

# csh -x
# echo foo {} bar
echo foo {} bar
foo {} bar
# echo `echo foo {} bar`
echo `echo foo {} bar`
echo foo {} bar
foo  bar

Sin embargo, las citas no ayudan:

# echo `echo foo '{}' bar`
echo `echo foo '{}' bar`
echo foo {} bar
foo  bar

Podría citar toda la sustitución del comando:

# echo "`echo foo {} bar`"
echo `echo foo {} bar`
echo foo {} bar
foo {} bar

Pero eso es pasar un argumento a ese eco externo.

En csho tcsh, debe citar el {}cuando no está solo, como en:

find . -name '*.txt' -type f -exec cp {} '{}.back' \;

(aunque ese tipo de finduso no es portátil ya que algunos finds solo se expanden {}por sí solos).

Stéphane Chazelas
fuente
1

En una palabra, csh. bashy otros shells modernos reconocen que el usuario probablemente no está solicitando una expansión de llaves nulas. (Modern cshes en realidad tcshy ahora también puede manejarlo {}sensualmente)

geekosaur
fuente
Es bueno escucharlo, pero estoy pidiendo lo contrario, un caparazón que no lo maneja con sensatez.
usuario desconocido
Estás asumiendo que alguien entraría y extraería la referencia de la página de información a citas adicionales solo porque los sistemas Linux tienen todos los más nuevos csh. No todos ejecutan utilidades GNU en Linux; de hecho, es bastante común instalarlos en sistemas comerciales Unix más antiguos cuyos comandos agrupados son limitados. (Reemplazar cshesos sistemas es menos probable, porque los scripts del sistema pueden depender de las idiosincrasias del original csh, e incluso si todos los usuarios estuvieran configurados para usar uno más nuevo csh, rootcasi seguramente necesitarían seguir siendo la versión incluida.)
geekosaur
2
No. Estoy en un debate con un tipo que dice que en nuestra wiki deberíamos dar consejos para usar "{}" todo el tiempo, porque la página del manual lo dice. Y ahora me gustaría saber si hay un shell que no estoy usando, como ksh, zsh, tcsh, csh o XYZsh, que no sé, para lo cual esta afirmación es cierta, o si honestamente puedo suponer que no hay Si hay shells para diferentes Unixes que necesitan el "{}", esa sería una buena explicación de por qué la oración todavía está en la página de manual, pero no es apropiada como consejo para principiantes de Linux.
usuario desconocido
Ahora me pregunto si pdkshhace lo correcto ... aunque la respuesta correcta a eso es probablemente "real kshes FOSS en estos días".
geekosaur
44
Pdksh, como mksh, ksh93, bash y zsh, solo expande llaves cuando hay una coma en el medio (o ..para ksh93, bash y zsh). Sólo se expande (t) csh {foo}a foo, e incluso deja {}solo (al menos en BSD de reciente).
Gilles 'SO- deja de ser malvado'