En realidad estás escribiendo un shell script, y el contexto AppleScript parece bastante superfluo por el momento. Si estás usando predominantemente comandos de shell, quizás quieras considerar escribir un script de shell en su lugar.
He empleado un poco de ambos porque cada uno tiene su utilidad en esta situación: los comandos de shell son necesarios para recuperar la información del disco a través de diskutil
ya que AppleScript no tiene ningún medio incorporado para obtener esa información por sí mismo; pero entonces, en lugar de perder el tiempo con grep
, awk
o sed
para que coincida con el patrón de texto de la salida sin procesar, podemos invocar la función -plist
para devolver la salida como datos de lista de propiedades con formato XML, que AppleScript puede manejar muy bien.
1. Recuperación de una lista de discos y volúmenes locales
Los discos y volúmenes conectados localmente tienen un inodo blockdevice en la carpeta /dev
por lo que obtener una lista de discos es bastante fácil con AppleScript:
use application "System Events"
property devices : a reference to files in folder "/dev"
property disknodes : a reference to (devices whose name begins with "disk")
set diskinfo to name of disknodes
Con mi unidad USB conectada, obtengo esta lista de inodos:
--> {"disk0", "disk0s1", "disk0s2", "disk1", "disk1s1",
"disk1s2", "disk1s3", "disk1s4", "disk2", "disk2s1"}
2. Aislar los volúmenes deseados
Para filtrar la lista de forma que sólo ejecutemos las utilidades adecuadas en los discos o volúmenes adecuados, diskutil
hace ahora su parte. Podemos crear un proceso shell desde dentro de AppleScript en lugar de hacerlo a través de Terminal . El comando de shell correspondiente es el siguiente:
diskutil info -plist <disk|volume>
Para ejecutar esto desde AppleScript, utilice la función do shell script
mando:
do shell script "diskutil info -plist" & id
donde id
es el nombre de un disco recuperado anteriormente y almacenado en diskinfo
. No puedes pasar toda la lista de una vez, así que tenemos que iterar a través de la lista y ejecutar el comando una vez para cada elemento:
use scripting additions
delete every property list item
repeat with id in diskinfo
set shellcmd to "diskutil info -plist " & id
set plist to do shell script shellcmd
Con los datos de la lista de propiedades devueltos por diskutil
AppleScript puede convertirlo en un property list
objeto. Los datos de la lista de propiedades devueltos al invocar diskutil info
está todo contenido en un único nivel, por lo que no hay que atravesar ninguna jerarquía. Lo mejor de property list
objetos es que AppleScript puede transformarlos en un record
lo que facilita enormemente la recuperación de valores:
make property list item with properties {name:id, text:plist}
tell the value of property list item id as record to if ¬
(its |Ejectable|) and (not its WholeDisk) and ¬
(its MountPoint = "") then set the contents of id ¬
to its {disk:ParentWholeDisk, volume:DeviceIdentifier}
end repeat
En una sola línea, AppleScript ha leído los datos de la lista de propiedades y ha descartado cualquier disco que esté:
- No eyectable;
- Es un disco "entero" (es decir, no un volumen montable);
- Tiene un punto de montaje identificable en el sistema.
También elimina todos los datos inútiles que no necesitamos y sólo devuelve el nombre de la partición y su disco principal. Éstos sólo serán eyectable (es decir, no los discos de recuperación y arranque, ni el disco duro de su ordenador), y serán volúmenes montables que no están montados actualmente. Para mí, y mi única unidad USB, que desmonté primero, me devolvió esta lista de discos:
set unmounted to records of diskinfo
--> {{disk:"disk2", volume:"disk2s1"}}
3. Montaje y expulsión de cada volumen
El análisis de los datos ya está hecho, así que ahora puede ejecutar diskutil
en los discos correspondientes. Puede elegir entre montar volúmenes individuales o montar un disco entero (que montará todos sus volúmenes). El método es el mismo para cada uno: iterar a través de nuestra lista de registros almacenada en la variable unmounted
y utilice disk
o el volume
de cada elemento como argumento para el comando shell apropiado:
diskutil mount <volume>
diskutil mountDisk <disk>
diskutil eject <disk>
El bucle AppleScript tiene el siguiente aspecto:
repeat with diskitem in unmounted
set shellcmd to "diskutil mount " & diskitem's volume
try
do shell script shellcmd
on error
set shellcmd to "diskutil eject " & diskitem's disk
try
do shell script shellcmd
end try
end try
end repeat
do shell script
devuelve en caso de éxito la salida a stdout
. Después de montar con éxito mi disco, el panel de mensajes en script Editor contenido:
"Volume USB on disk2s1 mounted"
No he provocado una situación que obligue a mi unidad a no montarse, pero normalmente, al fallar cualquier comando de shell (es decir, siempre que un comando de shell devuelva un estado distinto de cero), do shell script
arroja un error. Esto será detectado por el try
que redirigirá el script para ejecutar otro comando de shell responsable de expulsar la unidad. Esto también está en su propio try
por si también devuelve un estado distinto de cero, en cuyo caso el script continuará con el siguiente volumen.
A continuación se muestra el script final con algunas modificaciones adicionales. El script devolverá la lista original de registros desmontados con información extra añadida a cada elemento del disco informando del resultado del intento de montar y/o expulsar el volumen. Un fallo completo será simplemente reportado con false
. Así que mi resultado final script se parecía a esto:
--> {{disk:"disk2", volume:"disk2s1", result:"Volume USB on disk2s1 mounted"}}
El script final
use application "System Events"
property devices : a reference to files in folder "/dev"
property disknodes : a reference to (devices whose name begins with "disk")
set diskinfo to name of disknodes
use scripting additions
delete every property list item
repeat with id in diskinfo
set shellcmd to "diskutil info -plist " & id
set plist to do shell script shellcmd
make property list item with properties {name:id, text:plist}
tell the value of property list item id as record ¬
to if (its |Ejectable|) ¬
and (not its WholeDisk) ¬
and (its MountPoint = "") then set ¬
id's contents to {disk:its ParentWholeDisk ¬
, volume:its DeviceIdentifier ¬
, result:missing value}
end repeat
set unmounted to records of diskinfo
repeat with diskitem in unmounted
set shellcmd to "diskutil mount " & diskitem's volume
try
do shell script shellcmd
set diskitem's result to the result
on error
set shellcmd to "diskutil eject " & diskitem's disk
try
do shell script shellcmd
set diskitem's result to the result
on error E
set disk item's result to E
end try
end try
end repeat
return unmounted
0 votos
Yo intentaría montar todo lo que encuentre. Echa un vistazo a los bloques "Try...End Try". Tienen la ventaja de fallar silenciosamente. Esto puede ahorrarte un MONTÓN de "if exists..." y otros códigos repetitivos. Ten en cuenta que es mejor poner tus sentencias try-end try en después de consigues que funcione el algoritmo básico los pongo, luego --comento después de probar cada parte del script. Es agradable no obtener un diálogo script de Apple, cuando estás tratando de leer un libro.
0 votos
Yo, por principio, haría caso omiso de los consejos que da @WayfaringStranger aquí. En general no cómo utilizar ni por qué se implementaría la captura de errores, y un montón de "repetitivos".
if exists...
"La codificación puede ser laboriosa, sin duda, pero es absolutamente la forma correcta de hacer las cosas. Pero, como @Rottjung es un programador de C++, sospecho que ya lo sabe, así que este comentario va dirigido a otros lectores en general que no quieran adquirir malos hábitos en el scripting.0 votos
@CJK Espere a llegar a cinco páginas en su "si existe". Al igual que la antigua controversia "go to", los bloques Try-End try pueden deshacerse de montones masivos de código feo, y darte un producto mucho más legible y mantenible.
0 votos
@WayfaringStranger Como con cualquier cosa en la vida, cuando uno entiende cómo y por qué existe una regla, entonces uno sabe precisamente cómo y cuándo romper la regla de manera efectiva. Pero personalmente no me apresuraría a animar a los novatos en scripting a hacer esto, dado que normalmente confían mucho en esta pobre técnica, causando un montón de problemas en sus scripts que se solucionan eliminando cada
try
bloque. También supongo que si tienes que comprobar la existencia de cosas en cinco páginas, probablemente haya una forma mejor de enfocar tu tarea.0 votos
Sí, tienes razón cuando dices que los novatos toman el camino trillado. Llevo haciendo scripts desde que salió AppleScript, y hay veces en las que condensar montones de código en unas pocas sentencias es lo mejor. Me cansa ver demasiados "If thens" anidados. El bloque "try...end try" también es a veces una herramienta útil a la hora de depurar. Cuando las cosas empiezan a ir mal, los bloques "try...end try" se comentan fácilmente, lo que puede facilitar la localización de un error fatal. Es mejor asegurarse de que tienes el código bien escrito antes de encerrarlo en un bloque "try end try".