Ejecutar un comando en una nueva ventana de Terminal de Mac OS X


92

He estado tratando de averiguar cómo ejecutar un comando bash en una nueva ventana de Max OS X Terminal.app. Como ejemplo, así es como ejecutaría mi comando en un nuevo proceso de bash:

bash -c "my command here"

Pero esto reutiliza la ventana de terminal existente en lugar de crear una nueva. Quiero algo como:

Terminal.app -c "my command here"

Pero, por supuesto, esto no funciona. Soy consciente del comando "open -a Terminal.app", pero no veo cómo enviar argumentos a la terminal, o incluso si hice qué argumentos usar.


Siempre puede abrir las preferencias, ir a la pestaña "Perfiles", ir a la página "Shell" y configurar el comando de inicio allí. Solo se ejecuta cuando se abre la aplicación, ¡pero funciona mejor que las alternativas hacky!
Zane Helton

También vea la misma pregunta en superuser superuser.com/q/174576/122841
Beetle

Respuestas:


96

Una forma en la que puedo pensar en hacerlo es crear un archivo .command y ejecutarlo así:

echo echo hello > sayhi.command; chmod +x sayhi.command; open sayhi.command

o use applecript:

osascript -e 'tell application "Terminal" to do script "echo hello"'

aunque tendrá que escapar de muchas comillas dobles o no podrá usar comillas simples


Me decidí por el primer método. Es bastante hack-ish, pero funciona y no tengo que preocuparme por escapar de las comillas en mi comando ni nada. Gracias.
Walt D

3
Si el comando debe ejecutarse parametrizado, quizás desee intercambiar las comillas simples y dobles; aunque debe comprender la diferencia para poder hacer esto correctamente. osascript -e "tell application \"Terminal\" to do script \"echo '$variable'\"'
tripleee

¿Alguien sabe cómo cerrar la estúpida ventana de terminal sobrante?
Nicholas DiPiazza

Agregue ; exital final de los comandos del script de shell, como do script "echo hello; exit". Aún necesita cerrar la ventana por separado.
tripleee

65

Solución parcial:

Pon las cosas que quieres que se hagan en un script de shell, así

#!/bin/bash
ls
echo "yey!"

Y no olvides ' chmod +x file' para hacerlo ejecutable. Entonces tú puedes

open -a Terminal.app scriptfile

y se ejecutará en una nueva ventana. Agregue ' bash' al final de la secuencia de comandos para evitar que la nueva sesión salga. (Aunque es posible que tenga que averiguar cómo cargar los archivos rc de los usuarios y todo eso ...)


7
El contexto de la ventana recién abierta parece ser la carpeta raíz del usuario /Users/{username}. ¿Hay alguna forma de mantener la carpeta de contexto igual que la ventana de la terminal principal que la abrió?
Johnny Oshika

> Aunque es posible que tenga que averiguar cómo cargar los archivos rc de los usuarios y todo eso ... use bash -l para esto
Aivar

35

He estado intentando hacer esto por un tiempo. Aquí hay un script que cambia al mismo directorio de trabajo, ejecuta el comando y cierra la ventana del terminal.

#!/bin/sh 
osascript <<END 
tell application "Terminal"
    do script "cd \"`pwd`\";$1;exit"
end tell
END

No le gusta el script aceptado, su único problema de directorio actual es resolver ¡Gracias!
Marboni

Gran solucion; sin embargo, genera algo como tab 1 of window id 7433stdout en el shell de lanzamiento . Para suprimir eso, coloque >/dev/null antes <<END .
mklement0

1
Esto no cierra inherentemente la ventana de la terminal. Simplemente sale del intérprete de comandos. Terminal.app debe configurarse para cerrar la ventana automáticamente o al salir limpia del intérprete de comandos.
user66001

8

En caso de que a alguien le importe, aquí hay un equivalente para iTerm:

#!/bin/sh
osascript <<END
tell application "iTerm"
 tell the first terminal
  launch session "Default Session"
  tell the last session
   write text "cd \"`pwd`\";$1;exit"
  end tell
 end tell
end tell
END

3

Aquí hay otra versión (también usando AppleScript):

function newincmd() { 
   declare args 
   # escape single & double quotes 
   args="${@//\'/\'}" 
   args="${args//\"/\\\"}" 
   printf "%s" "${args}" | /usr/bin/pbcopy 
   #printf "%q" "${args}" | /usr/bin/pbcopy 
   /usr/bin/open -a Terminal 
   /usr/bin/osascript -e 'tell application "Terminal" to do script with command "/usr/bin/clear; eval \"$(/usr/bin/pbpaste)\""' 
   return 0 
} 

newincmd ls 

newincmd echo "hello \" world" 
newincmd echo $'hello \' world' 

ver: codesnippets.joyent.com/posts/show/1516


3

Hice una versión funcional de la respuesta de Oscar, esta también copia el entorno y los cambios en el directorio apropiado

function new_window {
    TMP_FILE=$(mktemp "/tmp/command.XXXXXX")
    echo "#!/usr/bin/env bash" > $TMP_FILE

    # Copy over environment (including functions), but filter out readonly stuff
    set | grep -v "\(BASH_VERSINFO\|EUID\|PPID\|SHELLOPTS\|UID\)" >> $TMP_FILE

    # Copy over exported envrionment
    export -p >> $TMP_FILE

    # Change to directory
    echo "cd $(pwd)" >> $TMP_FILE

    # Copy over target command line
    echo "$@" >> $TMP_FILE

    chmod +x "$TMP_FILE"
    open -b com.apple.terminal "$TMP_FILE"

    sleep .1 # Wait for terminal to start
    rm "$TMP_FILE"
}

Puedes usarlo así:

new_window my command here

o

new_window ssh example.com

1
La línea TMP_FILE="tmp.command"podría ser un problema si inicia más de un proceso simultáneamente. Sugeriría reemplazarlo porTMP_FILE=$(mktemp "/tmp/command.XXXXXX")
duthen

2

Aquí está mi increíble script, crea una nueva ventana de terminal si es necesario y cambia al directorio en el que se encuentra Finder si Finder está al frente. Tiene toda la maquinaria que necesitas para ejecutar comandos.

on run
    -- Figure out if we want to do the cd (doIt)
    -- Figure out what the path is and quote it (myPath)
    try
        tell application "Finder" to set doIt to frontmost
        set myPath to finder_path()
        if myPath is equal to "" then
            set doIt to false
        else
            set myPath to quote_for_bash(myPath)
        end if
    on error
        set doIt to false
    end try

    -- Figure out if we need to open a window
    -- If Terminal was not running, one will be opened automatically
    tell application "System Events" to set isRunning to (exists process "Terminal")

    tell application "Terminal"
        -- Open a new window
        if isRunning then do script ""
        activate
        -- cd to the path
        if doIt then
            -- We need to delay, terminal ignores the second do script otherwise
            delay 0.3
            do script " cd " & myPath in front window
        end if
    end tell
end run

on finder_path()
    try
        tell application "Finder" to set the source_folder to (folder of the front window) as alias
        set thePath to (POSIX path of the source_folder as string)
    on error -- no open folder windows
        set thePath to ""
    end try

    return thePath
end finder_path

-- This simply quotes all occurrences of ' and puts the whole thing between 's
on quote_for_bash(theString)
    set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to "'"
    set the parsedList to every text item of theString
    set AppleScript's text item delimiters to "'\\''"
    set theString to the parsedList as string
    set AppleScript's text item delimiters to oldDelims
    return "'" & theString & "'"
end quote_for_bash

1

Un colega me preguntó cómo abrir MUCHAS sesiones ssh a la vez. Usé la respuesta de cobbal para escribir este script:

tmpdir=$( mktemp -d )
trap '$DEBUG rm -rf $tmpdir ' EXIT
index=1

{
cat <<COMMANDS
ssh user1@host1
ssh user2@host2
COMMANDS
} | while read command
do 
  COMMAND_FILE=$tmpdir/$index.command
  index=$(( index + 1 ))
  echo $command > $COMMAND_FILE
  chmod +x  $COMMAND_FILE
  open $COMMAND_FILE
done
sleep 60

Al actualizar la lista de comandos (no tienen que ser invocaciones ssh), obtendrá una ventana abierta adicional para cada comando ejecutado. El sleep 60al final está ahí para mantener los .commandarchivos mientras se ejecutan. De lo contrario, el shell se completa demasiado rápido, ejecutando la trampa para eliminar el directorio temporal (creado por mktemp) antes de que las sesiones iniciadas tengan la oportunidad de leer los archivos.


0

También puede invocar la nueva función de comando de Terminal presionando la Shift + ⌘ + Ncombinación de teclas. El comando que coloque en el cuadro se ejecutará en una nueva ventana de Terminal.


Por supuesto que es útil saberlo, pero necesito hacerlo programáticamente. Hacer que mi programa genere un archivo .command y luego lo abra es una solución razonable (aunque un poco pirateada).
Walt D

0

A este guión lo llamo trun. Sugiero ponerlo en un directorio en su ruta ejecutable. Asegúrese de que sea ejecutable así:

chmod +x ~/bin/trun

Luego, puede ejecutar comandos en una nueva ventana simplemente agregando trun antes de ellos, así:

trun tail -f /var/log/system.log

Aquí está el guión. Hace algunas cosas elegantes como pasar sus argumentos, cambiar la barra de título, limpiar la pantalla para eliminar el desorden de inicio de la shell, eliminar su archivo cuando haya terminado. Al usar un archivo único para cada nueva ventana, se puede usar para crear muchas ventanas al mismo tiempo.

#!/bin/bash
# make this file executable with chmod +x trun
# create a unique file in /tmp
trun_cmd=`mktemp`
# make it cd back to where we are now
echo "cd `pwd`" >$trun_cmd
# make the title bar contain the command being run
echo 'echo -n -e "\033]0;'$*'\007"' >>$trun_cmd
# clear window
echo clear >>$trun_cmd
# the shell command to execute
echo $* >>$trun_cmd
# make the command remove itself
echo rm $trun_cmd >>$trun_cmd
# make the file executable
chmod +x $trun_cmd

# open it in Terminal to run it in a new Terminal window
open -b com.apple.terminal $trun_cmd

1
El uso incorrecto de $*todo arruinará cualquier cita no trivial en la entrada. Tú quieres en su "$@"lugar.
tripleee

Yo estaba buscando open -b com.apple.terminal. Gracias
Ebru Yener
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.