30

I have found way to open new tab in iTerm:

newtabi()
{
    osascript -e 'tell application "iTerm" to activate' -e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down'
}

And I want to execute some commands in new tab. Let it be simple command pwd. How to do it?

If I run ...

newtabi && pwd

The new tab is opened as expected, but pwd command is executed not in new tab but in old one, where I typed newtabi && pwd

I use zsh. My os is OS X 10.8.5

1
  • That question is 8 years old and the accepted answer is far from ideal. I still hope to see passing some arguments to open -a iTerm to run a command.
    – sorin
    Oct 28, 2021 at 9:58

6 Answers 6

22
+50

Use tell session -1 of current terminal to write text "pwd":

activate application "iTerm"
tell application "System Events" to keystroke "t" using command down
tell application "iTerm" to tell session -1 of current terminal to write text "pwd"
5
  • 1
    if opened more than one iTerms, it opens new tab in correct iTerm instance, but writes pwd in last tab of first instance of iTerm Nov 27, 2013 at 15:16
  • 2
    @efr You're right. I edited the answer to change terminal 1 to current terminal. Now it should run the command in the current window instead of the window that was opened first.
    – Lri
    Nov 27, 2013 at 15:40
  • 2
    This did not work on mac 10.11.6 with iTerm2. In script editor, "Unexpected end of line but found identifier" and "terminal" is highlighted Sep 5, 2017 at 15:50
  • 1
    This no longer works on macOS 10.14: execution error: System Events got an error: osascript is not allowed to send keystrokes. (1002) Feb 3, 2019 at 22:48
  • @KernelSanders You need to allow iterm to access System event by checking iTerm in MacOS Settings -> Security -> Accessibility
    – ReaperSoon
    Aug 12, 2020 at 8:01
21

As of at least macos Mojave, iTerm 3.2.8, if you run:

$ open -a iTerm .

It will add it as a tab to the current window.

6
  • 4
    can you then execute commands in the new terminal with this?
    – sf8193
    Sep 11, 2020 at 23:29
  • worked for me for iTerm with zsh Nov 24, 2020 at 11:24
  • Any ways to also pass the command to execute?
    – sorin
    Oct 28, 2021 at 9:59
  • 1
    @sorin Yes, if you pass a path to a binary as the final argument (instead of . in the answer), it will execute it (after a confirmation prompt from iTerm)
    – ultrafez
    Nov 15, 2021 at 18:10
  • Thank you! When I'm willing to create a new tab (sometimes I want a separate window), this is far and away the easiest solution. It avoids having to deal with an idiosyncratic scripting language. (Applescript is supposed to be easy and intuitive, but the fact that it is not like any commonly used language makes it non-intuitive.)
    – Mars
    Sep 4, 2022 at 17:42
20
osascript \
-e 'tell application "iTerm" to activate' \
-e 'tell application "System Events" to tell process "iTerm" to keystroke "t" using command down' \
-e 'tell application "System Events" to tell process "iTerm" to keystroke "ls"' \
-e 'tell application "System Events" to tell process "iTerm" to key code 52'
2
  • I'm getting 148:156: syntax error: Expected end of line but found identifier. (-2741) on this. Apr 12, 2019 at 15:32
  • 1
    what is key code 52 supposed to do?
    – Mausy5043
    Aug 29, 2021 at 7:59
18

I couldn't get the accepted answer to work. I also wanted to pass multiple commands. This is what I came up with.

newtabi(){  
  osascript \
    -e 'tell application "iTerm2" to tell current window to set newWindow to (create tab with default profile)'\
    -e "tell application \"iTerm2\" to tell current session of newWindow to write text \"${@}\""
}

Example usage

newtabi pwd
newtabi 'cd ~/ && pwd'
newtabi 'echo \"Hello New Tab\"'

For more complex actions, I would recommend breaking up the commands.

code_folder="/path/to/code"

alias project="cd ${code_folder}/my-project/foo && yarn run start"

Then, newtabi project

5
  • Can you please tell me how I can pass my current pwd to make the command look like: `newtabi '$PWD/foo && yarn run start'?
    – Raj
    Oct 18, 2019 at 19:39
  • 1
    @Raj, good question. I personally break up the commands into more consumable aliases and/or functions. I added another example. Hope it helps! Oct 31, 2019 at 16:18
  • 2
    @Raj newtabi "$(pwd)/foo && yarn run start"
    – darronz
    May 27, 2020 at 9:40
  • this is the best answer, should be top-rated
    – user207863
    Oct 21, 2022 at 16:21
  • 1
    I find it works better to use write text \"$*\" instead of ${@}. It lets you do newtabi cmd arg1 ... without having to use single quotes around the cmd and its arg. For args that contain spaces, you have to use the awkward syntax newtabi cmd '\"arg with space\"'. But with the original ${@} I couldn't get args with spaces to work at all. Commands with quotes don't work in either case.
    – Lorrin
    Dec 14, 2022 at 0:46
2

ttab is a macOS and Linux CLI for opening a new terminal tab/window, optionally with a command to execute and/or display settings

From its samples:

# Open a new tab, execute a command, wait for a keypress, and exit.
ttab 'ls "$HOME/Library/Application Support"; echo Press a key to exit.; read -rsn 1; exit'
1
  • This is easily the best option!
    – MEMark
    Dec 11, 2022 at 19:27
0

In case you are searching for JXA version:

Reusable class
class iTerm {
  constructor() {
    this.app = Application("iTerm");
    this.window = this.app.currentWindow();
  }
  findOrCreateTab(tabName, command) {
    this.app.activate();
    let existingTab = this.findTab(tabName);
    if (existingTab == undefined) {
      existingTab = this.createTab(tabName);
      console.log(`Created new tab: ${tabName}`);
      existingTab.currentSession().write({ text: command });
    } else {
      existingTab.select();
      console.log(`Found existing tab: ${tabName}`);
    }
    return existingTab;
  }
  createTab(name) {
    const newTab = this.window.createTabWithDefaultProfile();
    const session = newTab.currentSession();
    session.name = name;
    // console.log(newTab.index());
    // console.log(newSession.name());
    // console.log(newSession.profileName());
    return newTab;
  }
  findTab(name) {
    const tabs = this.window.tabs();
    for (const tab of tabs) {
      let session = tab.currentSession();
      // Strange, but iTerm uses `profileName` instead of `name` for keeping manually assigned session name.
      const tabName = session.profileName();
      if (tabName === name) {
        return tab;
      }
    }
    return undefined;
  }
}

In case you are also interesting how to use ESM modules in JXA (via browserify), then here is a good example: https://github.com/johnelm/node-jxa. Plus https://www.npmjs.com/package/esmify.

After setting up you can import reusable class as show below:

import iTerm from "../lib/iTerm";

const app = new iTerm();
app.findOrCreateTab("MyTab", `cd "$HOME"`);

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .