在新的Mac OS X终端窗口中运行命令

98

我一直在尝试找出如何在新的Max OS X Terminal.app窗口中运行bash命令。例如,以下是如何在新的bash进程中运行我的命令:

bash -c "my command here"

但是这种方法会重复使用现有的终端窗口而不是创建一个新的。我希望能够像这样:

Terminal.app -c "my command here"

当然这样做是行不通的。我知道"open -a Terminal.app"命令,但我不知道如何将参数转发到终端,甚至如果我知道该使用哪些参数也不清楚。


1
你可以打开偏好设置,进入“配置文件”选项卡,进入“Shell”页面并在那里设置启动命令。它只在应用程序打开时运行,但比其他hacky替代方案更有效! - Zane Helton
还可以在超级用户网站上查看相同的问题:https://superuser.com/q/174576/122841 - Beetle
许多初学者认为他们想要这个,直到他们学会如何运行和操作后台进程。让一个命令在后台运行并将输出写入文件,可以让您从任何终端检查输出文件,并且可以优雅地扩展到比您可以同时打开的只读终端窗口中有用的进程数量更多。 - undefined
11个回答

108

我能想到的一种方法是创建一个 .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
4
如果需要运行带参数的命令,也许你需要交换单引号和双引号;但你需要理解它们之间的区别,才能正确地进行操作。osascript -e "tell application \"Terminal\" to do script \"echo '$variable'\"" - tripleee
有人知道如何关闭那个烦人的剩余终端窗口吗? - Nicholas DiPiazza
在shell脚本命令的末尾添加; exit,例如do script "echo hello; exit"。您仍然需要单独关闭窗口。 - tripleee

68

部分解决方案:

将想要执行的任务放入一个 shell 脚本中,像这样:

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

别忘了 'chmod +x file' 使文件可执行。这样你就可以执行它了。

open -a Terminal.app scriptfile

并且它将在一个新窗口中运行。在脚本的末尾添加'bash'可以使新会话不退出。(虽然你可能需要想办法加载用户的rc文件和其他内容..)


7
新打开的窗口所处的上下文似乎是用户的根文件夹 /Users/{username}。有没有办法让上下文文件夹保持与打开它的父终端窗口相同? - Johnny Oshika
1
虽然您可能需要弄清楚如何加载用户的rc文件等内容,但可以使用bash -l来完成。 - Aivar

35

我已经尝试了一段时间。这里有一个脚本,可以切换到相同的工作目录,运行命令并关闭终端窗口。

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

不喜欢接受的脚本,您的解决方案解决了当前目录的问题。谢谢! - Marboni
很好的解决方案;它会在启动 shell 中向标准输出输出类似于 tab 1 of window id 7433 的内容。为了抑制这种情况,在 <<END 之前放置 >/dev/null - mklement0
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

5
这里有另一种方法(也使用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' 

请查看:codesnippets.joyent.com/posts/show/1516


这是唯一一个对我起作用的方案。我的需求是需要运行一个命令,创建一个ssh隧道到一台机器,然后打开一个vncloc来共享屏幕。我可以在一个新的bash函数中完美地完成这个过程,只需要三个步骤:在新窗口中运行隧道命令,等待3秒钟,然后在原始窗口中打开vncloc。完美地解决了问题。 - Tim Fuqua

3
我制作了一个函数版本的Oscar的答案,这个版本还会复制环境并切换到适当的目录。
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

3

这是我的神奇脚本,如果需要的话会创建一个新的终端窗口,并且切换到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

2

这里需要另一个选项 "ttab"

https://www.npmjs.com/package/ttab

安装
npm install ttab -g
使用方法:打开另一个标签页并运行ls
ttab ls
用法2:在目录~/dev中打开新标签页,背景为绿色,标题为“hi”,并运行code .
ttab -s Grass -t hi -d '~/dev' code .

很好的提示。可惜需要Nodejs,但好在它是跨平台的! - Antonio

2

我的一位同事问我如何同时打开大量 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文件。否则,shell会太快地完成,执行陷阱以删除临时目录(由mktemp创建),而启动的会话没有机会读取文件。


1

我将这个脚本称为trun。建议将其放在可执行路径的目录中。确保它是可执行的,如下所示:

chmod +x ~/bin/trun

然后,您只需在命令前添加 trun 即可在新窗口中运行命令,如下所示:
trun tail -f /var/log/system.log

这是脚本。它可以执行一些高级操作,例如传递参数、更改标题栏、清除屏幕以删除shell启动杂乱等。当完成时,它会删除自己的文件。通过为每个新窗口使用唯一的文件,它可以同时创建多个窗口。
#!/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

2
在整个过程中错误地使用 $* 将破坏输入中的任何非平凡引用。你应该使用 "$@" - tripleee
1
我正在寻找 open -b com.apple.terminal。谢谢。 - Ebru Yener

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接