¿Hacer eco de un comando de cola produce una salida inesperada?

8

Este comando, cuando se ejecuta solo, produce el resultado esperado (la última línea del crontab):

tail -n 1 /etc/crontab

Sin embargo, cuando lo ejecuto como parte de un comando echo para enviar el resultado a un archivo, agrega un resumen de todos los archivos en el directorio de trabajo, más el resultado esperado:

sudo bash -c 'echo $(tail -n 1 /etc/crontab) > /path/to/file'

¿Por qué este comando produjo los datos adicionales?

Davidw
fuente
3
¿Qué está echohaciendo por ti aquí? Considere tambiéntail -n 1 /etc/crontab | sudo tee /path/to/file >/dev/null
ctrl-alt-delor

Respuestas:

22

Su línea crontab tiene uno o más asteriscos *, que indica "en cualquier momento". Cuando esa línea se sustituye desde la sustitución del comando, el resultado es algo así como

echo * * * * * cmd > /path/to/file

Si bien la mayoría de las expansiones adicionales no se aplican a la salida de la sustitución de comandos, la expansión del nombre de ruta es (al igual que la división de campos) :

Los resultados de la sustitución de comandos no se procesarán para una mayor expansión de tilde, expansión de parámetros, sustitución de comandos o expansión aritmética. Si se produce una sustitución de comando entre comillas dobles, la división del campo y la expansión del nombre de ruta no se realizarán en los resultados de la sustitución.

La expansión de nombre de ruta es lo que se convierte *.txten una lista de nombres de archivo coincidentes (globbing), donde *coincide con todo. El resultado final es que obtienes cada nombre de archivo (no oculto) en el directorio de trabajo listado para cada uno *en tu línea crontab.


Puede solucionar esto citando la expansión, si el código que publicó fue un representante de un comando más complejo:

sudo bash -c 'echo "$(tail -n 1 /etc/crontab)" > /path/to/file'

pero de manera más directa solo pierdes por echocompleto:

sudo bash -c 'tail -n 1 /etc/crontab > /path/to/file'

Esto debería hacer lo que desea y también es más simple (la única otra diferencia material es que esta versión omitirá la división de campos que de otro modo habría ocurrido, por lo que las ejecuciones de espacios no se contraerán).

Michael Homer
fuente
55
Como / etc / crontab es casi siempre legible en todo el mundo, todo el truco "bash -c" es realmente innecesario: tail -n -1 /etc/crontab | sudo tee /path/to/filees el idioma que he encontrado que es el menos propenso a errores al redirigir la salida a archivos que requieren privilegios de superusuario.
Bajo
5

Consideremos un directorio con estos archivos:

$ ls
crontab  file1  file2  file3
$ cat crontab
f*

Ahora, ejecutemos el comando de cola:

$ tail -n 1 crontab
f*

Lo anterior es la última línea de crontaby esto es lo que esperamos. Sin embargo:

$ echo $(tail -n 1 crontab)
file1 file2 file3

Las comillas dobles eliminan este problema:

$ echo "$(tail -n 1 crontab)"
f*

Sin las comillas dobles, el shell expande el resultado de la sustitución del comando . Una de las expansiones es la expansión de nombre de ruta . En el caso anterior, esto significa que f*se expande para coincidir con cada nombre de archivo que comienza con f.

A menos que desee explícitamente expansiones de shell, coloque todas sus variables de shell y / o sustituciones de comandos entre comillas dobles.

John1024
fuente
4

el mecanismo de shell globing se expandirá *al archivo local.

es probable que la línea crontab tenga un *marcador de posición para cualquiera.

Por ejemplo, esta línea en crontab se ejecuta el domingo a las 7.47 a.m., la primera estrella significa cualquier día, la segunda cualquier mes.

47  7 * * 0 /run/on/sunday

entonces tú tail, y emitir

echo 47  7 * * 0 /run/on/sunday

eso se expandirá *al archivo local.

Archemar
fuente