Is it possible to open a new tab in Mac OS X's terminal from the command line in a currently opened tab?
I know that the keyboard shortcut to open a new tab in Terminal is "CMD+t" but I am looking for a script-based solution executed in the command line.
Update: This answer gained popularity based on the shell function posted below, which still works as of OSX 10.10 (with the exception of the -g option).
However, a more fully featured, more robust, tested script version is now available at the npm registry as CLI ttab, which also supports iTerm2:
If you have Node.js installed, simply run:
npm install -g ttab
(depending on how you installed Node.js, you may have to prepend sudo).
Otherwise, follow these instructions.
Once installed, run ttab -h for concise usage information, or man ttab to view the manual.
Building on the accepted answer, below is a bash convenience function for opening a new tab in the current Terminal window and optionally executing a command (as a bonus, there's a variant function for creating a new window instead).
If a command is specified, its first token will be used as the new tab's title.
Sample invocations:
# Get command-line help.
newtab -h
# Simpy open new tab.
newtab
# Open new tab and execute command (quoted parameters are supported).
newtab ls -l "$Home/Library/Application Support"
# Open a new tab with a given working directory and execute a command;
# Double-quote the command passed to `eval` and use backslash-escaping inside.
newtab eval "cd ~/Library/Application\ Support; ls"
# Open new tab, execute commands, close tab.
newtab eval "ls \$HOME/Library/Application\ Support; echo Press a key to exit.; read -s -n 1; exit"
# Open new tab and execute script.
newtab /path/to/someScript
# Open new tab, execute script, close tab.
newtab exec /path/to/someScript
# Open new tab and execute script, but don't activate the new tab.
newtab -G /path/to/someScript
CAVEAT: When you run newtab (or newwin) from a script, the script's initial working folder will be the working folder in the new tab/window, even if you change the working folder inside the script before invoking newtab/newwin - pass eval with a cd command as a workaround (see example above).
Source code (paste into your bash profile, for instance):
# Opens a new tab in the current Terminal window and optionally executes a command.
# When invoked via a function named 'newwin', opens a new Terminal *window* instead.
function newtab {
# If this function was invoked directly by a function named 'newwin', we open a new *window* instead
# of a new tab in the existing window.
local funcName=$FUNCNAME
local targetType='tab'
local targetDesc='new tab in the active Terminal window'
local makeTab=1
case "${FUNCNAME[1]}" in
newwin)
makeTab=0
funcName=${FUNCNAME[1]}
targetType='window'
targetDesc='new Terminal window'
;;
esac
# Command-line help.
if [[ "$1" == '--help' || "$1" == '-h' ]]; then
cat <<EOF
Synopsis:
$funcName [-g|-G] [command [param1 ...]]
Description:
Opens a $targetDesc and optionally executes a command.
The new $targetType will run a login shell (i.e., load the user's shell profile) and inherit
the working folder from this shell (the active Terminal tab).
IMPORTANT: In scripts, \`$funcName\` *statically* inherits the working folder from the
*invoking Terminal tab* at the time of script *invocation*, even if you change the
working folder *inside* the script before invoking \`$funcName\`.
-g (back*g*round) causes Terminal not to activate, but within Terminal, the new tab/window
will become the active element.
-G causes Terminal not to activate *and* the active element within Terminal not to change;
i.e., the previously active window and tab stay active.
NOTE: With -g or -G specified, for technical reasons, Terminal will still activate *briefly* when
you create a new tab (creating a new window is not affected).
When a command is specified, its first token will become the new ${targetType}'s title.
Quoted parameters are handled properly.
To specify multiple commands, use 'eval' followed by a single, *double*-quoted string
in which the commands are separated by ';' Do NOT use backslash-escaped double quotes inside
this string; rather, use backslash-escaping as needed.
Use 'exit' as the last command to automatically close the tab when the command
terminates; precede it with 'read -s -n 1' to wait for a keystroke first.
Alternatively, pass a script name or path; prefix with 'exec' to automatically
close the $targetType when the script terminates.
Examples:
$funcName ls -l "\$Home/Library/Application Support"
$funcName eval "ls \\\$HOME/Library/Application\ Support; echo Press a key to exit.; read -s -n 1; exit"
$funcName /path/to/someScript
$funcName exec /path/to/someScript
EOF
return 0
fi
# Option-parameters loop.
inBackground=0
while (( $# )); do
case "$1" in
-g)
inBackground=1
;;
-G)
inBackground=2
;;
--) # Explicit end-of-options marker.
shift # Move to next param and proceed with data-parameter analysis below.
break
;;
-*) # An unrecognized switch.
echo "$FUNCNAME: PARAMETER ERROR: Unrecognized option: '$1'. To force interpretation as non-option, precede with '--'. Use -h or --h for help." 1>&2 && return 2
;;
*) # 1st argument reached; proceed with argument-parameter analysis below.
break
;;
esac
shift
done
# All remaining parameters, if any, make up the command to execute in the new tab/window.
local CMD_PREFIX='tell application "Terminal" to do script'
# Command for opening a new Terminal window (with a single, new tab).
local CMD_NEWWIN=$CMD_PREFIX # Curiously, simply executing 'do script' with no further arguments opens a new *window*.
# Commands for opening a new tab in the current Terminal window.
# Sadly, there is no direct way to open a new tab in an existing window, so we must activate Terminal first, then send a keyboard shortcut.
local CMD_ACTIVATE='tell application "Terminal" to activate'
local CMD_NEWTAB='tell application "System Events" to keystroke "t" using {command down}'
# For use with -g: commands for saving and restoring the previous application
local CMD_SAVE_ACTIVE_APPNAME='tell application "System Events" to set prevAppName to displayed name of first process whose frontmost is true'
local CMD_REACTIVATE_PREV_APP='activate application prevAppName'
# For use with -G: commands for saving and restoring the previous state within Terminal
local CMD_SAVE_ACTIVE_WIN='tell application "Terminal" to set prevWin to front window'
local CMD_REACTIVATE_PREV_WIN='set frontmost of prevWin to true'
local CMD_SAVE_ACTIVE_TAB='tell application "Terminal" to set prevTab to (selected tab of front window)'
local CMD_REACTIVATE_PREV_TAB='tell application "Terminal" to set selected of prevTab to true'
if (( $# )); then # Command specified; open a new tab or window, then execute command.
# Use the command's first token as the tab title.
local tabTitle=$1
case "$tabTitle" in
exec|eval) # Use following token instead, if the 1st one is 'eval' or 'exec'.
tabTitle=$(echo "$2" | awk '{ print $1 }')
;;
cd) # Use last path component of following token instead, if the 1st one is 'cd'
tabTitle=$(basename "$2")
;;
esac
local CMD_SETTITLE="tell application \"Terminal\" to set custom title of front window to \"$tabTitle\""
# The tricky part is to quote the command tokens properly when passing them to AppleScript:
# Step 1: Quote all parameters (as needed) using printf '%q' - this will perform backslash-escaping.
local quotedArgs=$(printf '%q ' "$#")
# Step 2: Escape all backslashes again (by doubling them), because AppleScript expects that.
local cmd="$CMD_PREFIX \"${quotedArgs//\\/\\\\}\""
# Open new tab or window, execute command, and assign tab title.
# '>/dev/null' suppresses AppleScript's output when it creates a new tab.
if (( makeTab )); then
if (( inBackground )); then
# !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
if (( inBackground == 2 )); then # Restore the previously active tab after creating the new one.
osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_SAVE_ACTIVE_TAB" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$cmd in front window" -e "$CMD_SETTITLE" -e "$CMD_REACTIVATE_PREV_APP" -e "$CMD_REACTIVATE_PREV_TAB" >/dev/null
else
osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$cmd in front window" -e "$CMD_SETTITLE" -e "$CMD_REACTIVATE_PREV_APP" >/dev/null
fi
else
osascript -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$cmd in front window" -e "$CMD_SETTITLE" >/dev/null
fi
else # make *window*
# Note: $CMD_NEWWIN is not needed, as $cmd implicitly creates a new window.
if (( inBackground )); then
# !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
if (( inBackground == 2 )); then # Restore the previously active window after creating the new one.
osascript -e "$CMD_SAVE_ACTIVE_WIN" -e "$cmd" -e "$CMD_SETTITLE" -e "$CMD_REACTIVATE_PREV_WIN" >/dev/null
else
osascript -e "$cmd" -e "$CMD_SETTITLE" >/dev/null
fi
else
# Note: Even though we do not strictly need to activate Terminal first, we do it, as assigning the custom title to the 'front window' would otherwise sometimes target the wrong window.
osascript -e "$CMD_ACTIVATE" -e "$cmd" -e "$CMD_SETTITLE" >/dev/null
fi
fi
else # No command specified; simply open a new tab or window.
if (( makeTab )); then
if (( inBackground )); then
# !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
if (( inBackground == 2 )); then # Restore the previously active tab after creating the new one.
osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_SAVE_ACTIVE_TAB" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$CMD_REACTIVATE_PREV_APP" -e "$CMD_REACTIVATE_PREV_TAB" >/dev/null
else
osascript -e "$CMD_SAVE_ACTIVE_APPNAME" -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" -e "$CMD_REACTIVATE_PREV_APP" >/dev/null
fi
else
osascript -e "$CMD_ACTIVATE" -e "$CMD_NEWTAB" >/dev/null
fi
else # make *window*
if (( inBackground )); then
# !! Sadly, because we must create a new tab by sending a keystroke to Terminal, we must briefly activate it, then reactivate the previously active application.
if (( inBackground == 2 )); then # Restore the previously active window after creating the new one.
osascript -e "$CMD_SAVE_ACTIVE_WIN" -e "$CMD_NEWWIN" -e "$CMD_REACTIVATE_PREV_WIN" >/dev/null
else
osascript -e "$CMD_NEWWIN" >/dev/null
fi
else
# Note: Even though we do not strictly need to activate Terminal first, we do it so as to better visualize what is happening (the new window will appear stacked on top of an existing one).
osascript -e "$CMD_ACTIVATE" -e "$CMD_NEWWIN" >/dev/null
fi
fi
fi
}
# Opens a new Terminal window and optionally executes a command.
function newwin {
newtab "$#" # Simply pass through to 'newtab', which will examine the call stack to see how it was invoked.
}
Try this:
osascript -e 'tell application "Terminal" to activate' \
-e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
-e 'tell application "Terminal" to do script "echo hello" in selected tab of the front window'
But if you need to run dynamic command, set myCommand variable, and replace last line with:
-e "tell application \"Terminal\" to do script \"${myCommand};\" in selected tab of the front window";
osascript -e 'tell app "Terminal"
do script "echo hello"
end tell'
This opens a new terminal and executes the command "echo hello" inside it.
Here's how it's done by bash_it:
function tab() {
osascript 2>/dev/null <<EOF
tell application "System Events"
tell process "Terminal" to keystroke "t" using command down
end
tell application "Terminal"
activate
do script with command "cd \"$PWD\"; $*" in window 1
end tell
EOF
}
After adding this to your .bash_profile, you'd use the tab command to open the current working directory in a new tab.
See:
https://github.com/revans/bash-it/blob/master/plugins/available/osx.plugin.bash#L3
If you use oh-my-zsh (which every trendy geek should use), after activating the macos plugin in .zshrc, simply enter the tab command; it will open a new tab and cd in the directory your were on.
I added these to my .bash_profile so I can have access to tabname and newtab
tabname() {
printf "\e]1;$1\a"
}
new_tab() {
TAB_NAME=$1
COMMAND=$2
osascript \
-e "tell application \"Terminal\"" \
-e "tell application \"System Events\" to keystroke \"t\" using {command down}" \
-e "do script \"printf '\\\e]1;$TAB_NAME\\\a'; $COMMAND\" in front window" \
-e "end tell" > /dev/null
}
So when you're on a particular tab you can just type
tabname "New TabName"
to organize all the open tabs you have. It's much better than getting info on the tab and
changing it there.
I know this is an old post, but this worked for me:
open -a Terminal "`pwd`"
To run a command as requested below takes some jiggery:
echo /sbin/ping 8.8.8.8 > /tmp/tmp.sh;chmod a+x /tmp/tmp.sh;open -a Terminal /tmp/tmp.sh
The keyboard shortcut cmd-t opens a new tab, so you can pass this keystroke to OSA command as follows:
osascript -e 'tell application "System Events"' -e 'keystroke "t" using command down' -e 'end tell'
when you are in a terminal window,
command + n => opens a new terminal and
command + t => opens a new tab in current terminal window
open -n -a Terminal
and you can pass the target directory as parameter
open -n -a Terminal /Users
If you are using iTerm this command will open a new tab:
osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'
What about this simple snippet, based on a standard script command (echo):
# set mac osx's terminal title to "My Title"
echo -n -e "\033]0;My Title\007"
With X installed (e.g. from homebrew, or Quartz), a simple "xterm &" does (nearly) the trick, it opens a new terminal window (not a tab, though).
I made a simplified version that works around the AppleScript bug that generates a new UI.
on run argv
set scpt to first item in argv
set flag to application "Terminal" is not running
tell application "Terminal"
do script scpt
activate
delay 1.0E-5
if flag then close back window
end tell
end run
Or this also works
open -a Terminal.app path/file.sh
Another option is to use make to organize your terminal tab launching a little better. For example, you could create a make file that looks like this:
Makefile
.PHONY:launchtabgroup1
launchtabgroup1:
chmod u+r+x scripts/launch_tabgroup1.sh
scripts/launch_tabgroup1.sh
.PHONY:launchtabgroup2
launchtabgroup2:
chmod u+r+x scripts/launch_tabgroup2.sh
scripts/launch_tabgroup2.sh
Then within a subdirectory of where you store your make file, create a scripts directory that has all the commands to open whatever tabs you want for that group. Like this:
launch_tabgroup1.sh
#!/usr/bin/env sh
osascript -e 'tell application "Terminal" to activate' \
-e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
-e 'tell application "Terminal" to do script "cd ../path/to/desired/directory" in selected tab of the front window'
osascript -e 'tell application "Terminal" to activate' \
-e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
-e 'tell application "Terminal" to do script "cd ../path/to/desired/directory" in selected tab of the front window'
osascript -e 'tell application "Terminal" to activate' \
-e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
-e 'tell application "Terminal" to do script "cd ../path/to/desired/directory" in selected tab of the front window'
You would make corresponding entries in the Makefile that call the respective .sh files, which spin up the tabs in the group!
To actually spin up the tab groups navigate to the Makefile directory and run make launchtabgroup1 or make launchtabgroup2.
Related
I have the following Bash Script.
!#/bin/bash
fscanx --pdf /scandata/Trust_Report
if [ "$?" = "0" ]; then
I would like to run the following AppleScript
tell application "FileMaker Pro Advanced"
activate
show window "Trust Reports"
do script "Scan Trust Report"
end tell
else
say “It did not scan”
fi
What is the proper syntax to invoke this AppleScript?
Thank you
Use the osascript command. You can either pass the script as parameters using the -e flag, like this (note it's not necessary to break this into multiple lines, I just do that to make it more readable):
osascript \
-e 'tell application "FileMaker Pro Advanced"' \
-e 'activate' \
-e 'show window "Trust Reports"' \
-e 'do script "Scan Trust Report"' \
-e 'end tell'
Or pass it as a here document, like this:
osascript <<'EOF'
tell application "FileMaker Pro Advanced"
activate
show window "Trust Reports"
do script "Scan Trust Report"
end tell
EOF
BTW, you don't need to test $? in a separate command, you can include the command you're trying to check the success of directly in the if statement:
if fscanx --pdf /scandata/Trust_Report; then
osascript ...
else
say “It did not scan”
fi
I have function to change the title of a terminal. I wish to pass this to a osascript command.
I try:
function title {
name=hostname
printf "\033]0;$name %s\007" "$1"
}
osascript -e "tell application \"Terminal\"" \
-e "tell application \"System Events\" to keystroke \"t\" using {command down}" \
-e "do script \"title newTitle \" in front window" \
-e "end tell"
> /dev/null;
It does not work.
Any ideas?
Thanks
Is your function defined in your .bash_profile?
If so, this should work (below). If not, you need to add it, or else the new tab you create doesn't know about the function.
Multi-line scripts without $1 substitution can be put inside single quotes without escaping...
osascript -e 'tell application "Terminal"
tell application "System Events" to keystroke "t" using {command down}
do script "title NewTitle" in front window
end tell'
The > /dev/null doesn't seem to make much difference because of the text that is generated while launching the new tab.
I've got a shell script that I call that uses osascript, and that osascript calls a shell script and passes in a variable that I've set in the original shell script. I don't know how to pass that variable in from the applescript to shell script.
How can I pass in a variable from shell script to applescript to shell script...?
Let me know if I don't make sense.
i=0
for line in $(system_profiler SPUSBDataType | sed -n -e '/iPad/,/Serial/p' -e '/iPhone/,/Serial/p' | grep "Serial Number:" | awk -F ": " '{print $2}'); do
UDID=${line}
echo $UDID
#i=$(($i+1))
sleep 1
osascript -e 'tell application "Terminal" to activate' \
-e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down' \
-e 'tell application "Terminal" to do script "cd '$current_dir'" in selected tab of the front window' \
-e 'tell application "Terminal" to do script "./script.sh ip_address '${#UDID}' &" in selected tab of the front window'
done
Shell variables don't expand inside single quotes. When you to want pass a shell variable to osascript you need to use double "" quotes. The problem is, than you must escape double quotes needed inside the osascript, like:
the script
say "Hello" using "Alex"
you need escape quotes
text="Hello"
osascript -e "say \"$text\" using \"Alex\""
This not very readable, therefore it much better to use the bash's heredoc feature, like
text="Hello world"
osascript <<EOF
say "$text" using "Alex"
EOF
And you can write multiline script inside for a free, it is much better than using multiple -e args...
You can also use a run handler or export:
osascript -e 'on run argv
item 1 of argv
end run' aa
osascript -e 'on run argv
item 1 of argv
end run' -- -aa
osascript - -aa <<'END' 2> /dev/null
on run {a}
a
end run
END
export v=1
osascript -e 'system attribute "v"'
I don't know any way to get STDIN. on run {input, arguments} only works in Automator.
I need to automatically open terminal with multiple tabs and need to execute multiple commands on it.
I know how to do it in ubuntu. but its not working on mac os . Any Idea?
gnome-terminal --tab -e "tail -f somefile" --tab -e "some_other_command"
I can give you a clue.
Using osascript, you can do it.
UPDATE:
One thing you need to understand is, gnome-terminal is for Linux. Gnome is a very popular Desktop environment written for Linux.
Use this oneliner for opening a new tab with $PWD as the working directory:
osascript -e "tell application \"Terminal\"" -e "tell application \"System Events\" to keystroke \"t\" using {command down}" -e "do script \"cd $PWD; clear\" in front window" -e "end tell" > /dev/null
Following one liner will just open a new tab with $PWD as working directory and will execute echo Hi
osascript -e "tell application \"Terminal\"" -e "tell application \"System Events\" to keystroke \"t\" using {command down}" -e "do script \"cd $PWD; echo HI\" in front window" -e "end tell" > /dev/null
First off, this is my first go at shell scripting. I have only experience in HTML and CSS :]
What I want to do is setup a simple folder structure and prompt the user in a dialog box to set a name for the root folder. I'll trigger this shell script via an OS X Service or a Keyboard Maestro hotkey.
This is what I've come up with so far:
#!/bin/sh
echo -n "Enter the project name:"
read -e NAME
mkdir -p ~/Desktop/$NAME
mkdir -p ~/Desktop/$NAME/subfolder1
mkdir -p ~/Desktop/$NAME/subfolder2
Obviously there's some error - the variable won't get passed on and the root folder isn't created. I also read that I should use "dialog" to ask for the input, but I wasn't capable of writing something that works.
Your help is greatly appreciated. Thanks.
The echo and read commands both work via a text interface, such as the Terminal window the script is running in. If you run a script in an OS X Service, there's no Terminal-like interface, so neither command does anything useful. (I don't know about Keyboard Maestro, but I assume it's similar.) The simplest way to interact from a situation like this is generally to use AppleScript via the osascript command:
name="$(osascript -e 'Tell application "System Events" to display dialog "Enter the project name:" default answer ""' -e 'text returned of result' 2>/dev/null)"
if [ $? -ne 0 ]; then
# The user pressed Cancel
exit 1 # exit with an error status
elif [ -z "$name" ]; then
# The user left the project name blank
osascript -e 'Tell application "System Events" to display alert "You must enter a project name; cancelling..." as warning'
exit 1 # exit with an error status
fi
mkdir -p ~/Desktop/$name
mkdir -p ~/Desktop/$name/subfolder1
mkdir -p ~/Desktop/$name/subfolder2
(Note: I prefer to use lowercase variable names in the shell to avoid possible conflicts with special variables like PATH etc...)
Note: #Gordon Davisson's answer deserves the credit, but here are some improvements that make the code more robust (I tried to edit the original post, but it was rejected).
Improvements:
leading and trailing whitespace is trimmed from the input
whitespace-only input is prevented
empty or whitespace-only input returns user to prompt after warning
Code:
while :; do # Loop until valid input is entered or Cancel is pressed.
name=$(osascript -e 'Tell application "System Events" to display dialog "Enter the project name:" default answer ""' -e 'text returned of result' 2>/dev/null)
if (( $? )); then exit 1; fi # Abort, if user pressed Cancel.
name=$(echo -n "$name" | sed 's/^ *//' | sed 's/ *$//') # Trim leading and trailing whitespace.
if [[ -z "$name" ]]; then
# The user left the project name blank.
osascript -e 'Tell application "System Events" to display alert "You must enter a non-blank project name; please try again." as warning' >/dev/null
# Continue loop to prompt again.
else
# Valid input: exit loop and continue.
break
fi
done
I'm assuming the script isn't being run in an interactive shell, which means you will not be able to interact with read -e.
I don't have an OSX box to try this out, but you could try using CocoaDialog for a graphical dialog box.
Untested example:
#!/bin/bash
NAME=`CocoaDialog standard-inputbox --title "Project Name" --no-newline --no-cancel --informative-text "Enter the project name"`
mkdir -p ~/Desktop/$NAME/subfolder1
mkdir -p ~/Desktop/$NAME/subfolder2
Try changing the shebang line to #!/bin/bash. read is a bash builtin, and you're not running your script with bash currently. /bin/sh may actually be a link to /bin/bash, but according to Wikipedia this depends on the specific version of OS X that you have.
Your script as it is should work correctly under bash.
NAME is empty or it's just outputting $NAME as a string? If it's the latter you should try ~/Desktop/$(NAME)
Also, you should try XDialog (or whatever is the tool in MacOS.) There are plenty of resources about using it for input handling.