Запуск команды в новом окне терминала Mac OS X


92

Я пытался понять, как запустить команду bash в новом окне Max OS X Terminal.app. В качестве примера, вот как я бы запустил свою команду в новом процессе bash:

bash -c "my command here"

Но это повторно использует существующее окно терминала вместо создания нового. Я хочу что-то вроде:

Terminal.app -c "my command here"

Но, конечно, это не работает. Мне известна команда «open -a Terminal.app», но я не понимаю, как пересылать аргументы в терминал, и даже если я сделал, какие аргументы использовать.


Вы всегда можете открыть настройки, перейти на вкладку «Профили», перейти на страницу «Оболочка» и установить там команду запуска. Он запускается только при открытии приложения, но работает лучше, чем хакерские альтернативы!
Зейн Хелтон

Также см.
Beetle

Ответы:


96

один из способов, который я могу придумать, - это создать файл .command и запустить его так:

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

или используйте applescript:

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

хотя вам либо придется избегать большого количества двойных кавычек, либо вы не сможете использовать одинарные кавычки


Я остановился на первом способе. Это довольно взломанный вариант, но он работает, и мне не нужно беспокоиться об экранировании кавычек в моей команде или о чем-то еще. Спасибо.
Walt D,

3
Если команду нужно запускать параметризованно, возможно, вы захотите поменять местами одинарные и двойные кавычки; хотя вам нужно понимать разницу, чтобы делать это правильно. osascript -e "tell application \"Terminal\" to do script \"echo '$variable'\"'
Tripleee

кто-нибудь знает, как закрыть оставшееся глупое окно терминала?
Николас ДиПиацца

Добавьте ; exitв конце команд сценария оболочки, например do script "echo hello; exit". Окно еще нужно закрыть отдельно.
Tripleee

65

Частичное решение:

Поместите то, что вы хотите сделать, в сценарий оболочки, например

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

И не забудьте chmod +x fileсделать его исполняемым. Тогда ты можешь

open -a Terminal.app scriptfile

и он запустится в новом окне. Добавить ' bash' в конце скрипта, чтобы новый сеанс не завершился. (Хотя вам, возможно, придется придумать, как загружать пользовательские rc-файлы и прочее ..)


7
Контекст вновь открытого окна кажется корневой папкой пользователя /Users/{username}. Есть ли способ сохранить папку контекста такой же, как в родительском окне терминала, которое ее открыло?
Джонни Ошика

> Хотя вам, возможно, придется выяснить, как загружать rc-файлы пользователей и прочее .. используйте для этого bash -l
Айвар

35

Я пытался сделать это некоторое время. Вот сценарий, который переходит в тот же рабочий каталог, запускает команду и закрывает окно терминала.

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

Не нравится принятый скрипт, ваш единственный решит текущую проблему с каталогом. Благодарность!
Марбони

Отличное решение; однако он выводит что-то вроде tab 1 of window id 7433stdout в запускающей оболочке. Чтобы подавить это, поместите >/dev/null перед <<END .
mklement0 04

1
Это не закрывает окно терминала. Он просто выходит из командного интерпретатора. Terminal.app должен быть настроен на автоматическое закрытие окна или на чистый выход командного интерпретатора.
user66001

8

Если кому-то интересно, вот эквивалент для 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

Вот еще один вариант (также с использованием 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' 

см .: codenippets.joyent.com/posts/show/1516


3

Я сделал функциональную версию ответа Оскара, он также копирует среду и изменяется в соответствующий каталог

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

Вы можете использовать это так:

new_window my command here

или

new_window ssh example.com

1
Линия TMP_FILE="tmp.command"может стать проблемой, если вы запустите более одного процесса одновременно. Я бы предложил заменить его наTMP_FILE=$(mktemp "/tmp/command.XXXXXX")
duthen

2

Вот мой замечательный скрипт, он при необходимости создает новое окно терминала и переключается в каталог, в котором находится Finder, если Finder находится на переднем плане. В нем есть все необходимое для выполнения команд.

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

Коллега спросил меня, как открыть МНОГО сеансов ssh одновременно. Я использовал ответ Cobbal, чтобы написать этот скрипт:

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

Обновив список команд (они не обязательно должны быть вызовами ssh), вы получите дополнительное открытое окно для каждой выполненной команды. В sleep 60конце нужно хранить .commandфайлы во время их выполнения. В противном случае оболочка завершится слишком быстро, выполняя ловушку для удаления временного каталога (созданного mktemp) до того, как запущенные сеансы получат возможность прочитать файлы.


0

Вы также можете вызвать новую командную функцию Терминала, нажав Shift + ⌘ + Nкомбинацию клавиш. Команда, введенная вами в поле, будет запущена в новом окне терминала.


Конечно полезно знать, но мне нужно делать это программно. Если моя программа сгенерирует файл .command, а затем откроет его, это разумное (если немного хакерское) решение.
Walt D

0

Я называю этот скрипт trun. Я предлагаю поместить его в каталог на вашем исполняемом пути. Убедитесь, что это исполняемый файл следующим образом:

chmod +x ~/bin/trun

Затем вы можете запускать команды в новом окне, просто добавляя перед ними trun, например:

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

Вот сценарий. Он делает некоторые причудливые вещи, такие как передача ваших аргументов, изменение строки заголовка, очистка экрана, чтобы удалить беспорядок при запуске оболочки, удаление своего файла, когда это будет сделано. Используя уникальный файл для каждого нового окна, его можно использовать для создания нескольких окон одновременно.

#!/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
Неправильное использование $*повсюду разрушит любое нетривиальное цитирование во вводе. Вы хотите "$@"вместо этого.
tripleee

Я искал open -b com.apple.terminal. Спасибо
Эбру Йенер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.