Executar um comando em uma nova janela do Mac OS X Terminal


89

Estou tentando descobrir como executar um comando bash em uma nova janela do Max OS X Terminal.app. Como exemplo, aqui está como eu executaria meu comando em um novo processo bash:

bash -c "my command here"

Mas isso reutiliza a janela do terminal existente em vez de criar uma nova. Eu quero algo como:

Terminal.app -c "my command here"

Mas é claro que isso não funciona. Estou ciente do comando "open -a Terminal.app", mas não vejo como encaminhar argumentos para o terminal, ou mesmo se fiz quais argumentos usar.


Você sempre pode abrir as preferências, vá para a guia "Perfis", vá para a página "Shell" e defina o comando de inicialização lá. Ele só roda quando o aplicativo é aberto, mas funciona melhor que as alternativas hacky!
Zane Helton

Veja também a mesma pergunta em superuser superuser.com/q/174576/122841
Beetle

Respostas:


95

uma maneira que posso pensar para fazer isso de início é criar um arquivo .command e executá-lo assim:

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

ou use applecript:

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

embora você tenha que escapar de muitas aspas duplas ou não poderá usar aspas simples


Eu decidi pelo primeiro método. É bastante hack-ish, mas funciona e eu não preciso me preocupar em escapar de aspas no meu comando ou qualquer coisa. Obrigado.
Walt D

3
Se o comando precisar ser executado parametrizado, talvez você queira trocar as aspas simples e duplas; embora você precise entender a diferença para poder fazer isso corretamente. osascript -e "tell application \"Terminal\" to do script \"echo '$variable'\"'
tripleee

Alguém sabe como fechar a janela do terminal estúpida que sobrou?
Nicholas DiPiazza

Adicione ; exitno final dos comandos de script de shell, como do script "echo hello; exit". Você ainda precisa fechar a janela separadamente.
tripleee de

65

Solução parcial:

Coloque as coisas que você deseja fazer em um script de shell, assim

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

E não se esqueça de ' chmod +x file' para torná-lo executável. Então você pode

open -a Terminal.app scriptfile

e será executado em uma nova janela. Adicione ' bash' no final do script para evitar que a nova sessão seja encerrada. (Embora você possa ter que descobrir como carregar os arquivos rc dos usuários e outras coisas ..)


7
O contexto da janela recém-aberta parece ser a pasta raiz do usuário /Users/{username}. Existe alguma maneira de manter a pasta de contexto igual à janela do terminal pai que a abriu?
Johnny Oshika

> Embora você possa ter que descobrir como carregar os arquivos rc dos usuários e outras coisas .. use bash -l para isso
Aivar

35

Estou tentando fazer isso há um tempo. Aqui está um script que muda para o mesmo diretório de trabalho, executa o comando e fecha a janela do terminal.

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

Não gosta de script aceito, seu único problema de solução de diretório atual. Obrigado!
Marboni

Ótima solução; ele produz algo como tab 1 of window id 7433stdout no shell de inicialização . Para suprimir isso, coloque >/dev/null antes <<END .
mklement0

1
Isso não fecha inerentemente a janela do terminal. Ele simplesmente sai do interpretador de comandos. Terminal.app deve ser configurado para fechar a janela automaticamente ou na saída limpa do interpretador de comandos.
user66001

8

Caso alguém se importe, aqui está um 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

Aqui está mais uma visão disso (também 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' 

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


3

Fiz uma versão funcional da resposta do Oscar, esta também copia o ambiente e muda para o diretório apropriado

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"
}

Você pode usá-lo assim:

new_window my command here

ou

new_window ssh example.com

1
A linha TMP_FILE="tmp.command"pode ser um problema se você iniciar mais de um processo simultaneamente. Eu sugeriria substituí-lo porTMP_FILE=$(mktemp "/tmp/command.XXXXXX")
duthen

2

Aqui está meu script incrível, ele cria uma nova janela de terminal se necessário e muda para o diretório em que o Finder está se o Finder estiver na frente. Ele tem todo o mecanismo de que você precisa para executar 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

Um colega me perguntou como abrir MUITAS sessões de ssh de uma vez. Usei a resposta de Cobbal para escrever 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

Ao atualizar a lista de comandos (eles não precisam ser invocações ssh), você obterá uma janela aberta adicional para cada comando executado. O sleep 60no final existe para manter os .commandarquivos enquanto eles estão sendo executados. Caso contrário, o shell será concluído muito rapidamente, executando o trap para excluir o diretório temporário (criado por mktemp) antes que as sessões iniciadas tenham a oportunidade de ler os arquivos.


0

Você também pode invocar o novo recurso de comando do Terminal pressionando a Shift + ⌘ + Ncombinação de teclas. O comando que você colocar na caixa será executado em uma nova janela do Terminal.


Claro que é útil saber, mas preciso fazê-lo programaticamente. Fazer com que meu programa gere um arquivo .command e depois o abra é uma solução razoável (embora um pouco hackeada).
Walt D

0

Eu chamo esse script de trun. Eu sugiro colocá-lo em um diretório em seu caminho executável. Certifique-se de que seja executável assim:

chmod +x ~/bin/trun

Então, você pode executar comandos em uma nova janela apenas adicionando trun antes deles, como este:

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

Aqui está o script. Ele faz algumas coisas sofisticadas como passar seus argumentos, alterar a barra de título, limpar a tela para remover a desordem de inicialização do shell, remover seu arquivo quando estiver pronto. Usando um arquivo único para cada nova janela, ele pode ser usado para criar várias janelas ao mesmo tempo.

#!/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
O uso incorreto de $*todo irá destruir qualquer citação não trivial na entrada. Você quer em "$@"vez disso.
tripleee

Eu estava procurando open -b com.apple.terminal. Obrigado
Ebru Yener
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.