Create a gist now

Instantly share code, notes, and snippets.

Embed
Start Emacs.app from CLI
#!/usr/bin/perl
# Emacs starter for Emacs mac port
# Thanks to Aquamacs Project and David Reitter
my $args = "";
my $tmpfiles = "";
for my $f (@ARGV) {
$args .= '"'.$f.'" ';
$tmpfiles .= '"'.$f.'" ' if (! -e $f);
}
system("touch $args") if ($tmpfiles);
system("open -a /Applications/Emacs.app $args");
# delay deletion because AE drag&drop doesn't work with non-existing documents
system("(sleep 3; rm $tmpfiles) &") if ($tmpfiles);
exit;
@ptb

This comment has been minimized.

Show comment
Hide comment
@ptb

ptb May 19, 2015

I rewrote this as a shell script and added the capability to accommodate filenames with spaces:

#!/bin/sh

if [ -e '/Applications/Emacs.app' ]; then
  t=()

  if [ ${#@} -ne 0 ]; then
    while IFS= read -r file; do
      [ ! -f "$file" ] && t+=("$file") && /usr/bin/touch "$file"
      file=$(echo $(cd $(dirname "$file") && pwd -P)/$(basename "$file"))
      $(/usr/bin/osascript <<-END
        if application "Emacs.app" is running then
          tell application id (id of application "Emacs.app") to open POSIX file "$file"
        else
          tell application ((path to applications folder as text) & "Emacs.app")
            activate
            open POSIX file "$file"
          end tell
        end if
END
      ) &  # Note: END on the previous line may be indented with tabs but not spaces
    done <<<"$(printf '%s\n' "$@")"
  fi

  if [ ! -z "$t" ]; then
    $(/bin/sleep 10; for file in "${t[@]}"; do
      [ ! -s "$file" ] && /bin/rm "$file";
    done) &
  fi
else
  vim -No "$@"
fi

Since I switched from vi, I can't help but to type vi filename every time, so I saved this script as /usr/local/bin/vi. In the off chance I haven't yet installed Emacs.app, it uses the built in system vim instead.

ptb commented May 19, 2015

I rewrote this as a shell script and added the capability to accommodate filenames with spaces:

#!/bin/sh

if [ -e '/Applications/Emacs.app' ]; then
  t=()

  if [ ${#@} -ne 0 ]; then
    while IFS= read -r file; do
      [ ! -f "$file" ] && t+=("$file") && /usr/bin/touch "$file"
      file=$(echo $(cd $(dirname "$file") && pwd -P)/$(basename "$file"))
      $(/usr/bin/osascript <<-END
        if application "Emacs.app" is running then
          tell application id (id of application "Emacs.app") to open POSIX file "$file"
        else
          tell application ((path to applications folder as text) & "Emacs.app")
            activate
            open POSIX file "$file"
          end tell
        end if
END
      ) &  # Note: END on the previous line may be indented with tabs but not spaces
    done <<<"$(printf '%s\n' "$@")"
  fi

  if [ ! -z "$t" ]; then
    $(/bin/sleep 10; for file in "${t[@]}"; do
      [ ! -s "$file" ] && /bin/rm "$file";
    done) &
  fi
else
  vim -No "$@"
fi

Since I switched from vi, I can't help but to type vi filename every time, so I saved this script as /usr/local/bin/vi. In the off chance I haven't yet installed Emacs.app, it uses the built in system vim instead.

@railwaycat

This comment has been minimized.

Show comment
Hide comment
@railwaycat

railwaycat May 30, 2015

Thanks @ptb :)

Owner

railwaycat commented May 30, 2015

Thanks @ptb :)

@lmartins

This comment has been minimized.

Show comment
Hide comment
@lmartins

lmartins Jun 8, 2015

Sorry to ask guys, how can I install this?

lmartins commented Jun 8, 2015

Sorry to ask guys, how can I install this?

@InAbsentia

This comment has been minimized.

Show comment
Hide comment
@InAbsentia

InAbsentia Jun 30, 2015

@lmartins Paste into a file named emacs (or whatever you want the command to be) in a directory in your path (ahead of the location of the existing emacs command). Then chmod +x /path/to/emacs.

@lmartins Paste into a file named emacs (or whatever you want the command to be) in a directory in your path (ahead of the location of the existing emacs command). Then chmod +x /path/to/emacs.

@jasonm23

This comment has been minimized.

Show comment
Hide comment
@jasonm23

jasonm23 Oct 1, 2016

It'd be better to use emacsclient (and kick off Emacs.app as the alternate editor if it's not running already.)

jasonm23 commented Oct 1, 2016

It'd be better to use emacsclient (and kick off Emacs.app as the alternate editor if it's not running already.)

@tasmo

This comment has been minimized.

Show comment
Hide comment
@tasmo

tasmo Oct 25, 2016

The command open with the argument -a does not need the full application path. It's not useless but if you have installed Emacs in a different location I would consider just to use

system("open -a Emacs $args");

tasmo commented Oct 25, 2016

The command open with the argument -a does not need the full application path. It's not useless but if you have installed Emacs in a different location I would consider just to use

system("open -a Emacs $args");
@mwolson

This comment has been minimized.

Show comment
Hide comment
@mwolson

mwolson Nov 24, 2016

Here's a variation that ensures Emacs.app is launched, waits for it to be ready, and then uses emacsclient to open the file in a GUI frame and wait for edits to be completed.

#!/bin/sh

CLIENT=/Applications/Emacs.app/Contents/MacOS/bin/emacsclient

$(/usr/bin/osascript <<-END
  tell application ((path to applications folder as text) & "Emacs.app")
    activate
  end tell
END
)

while ! "$CLIENT" -e '"pass"' 2>&1 | grep '"pass"' > /dev/null; do
    sleep 0.3
done

if test "$#" -gt 0; then
    exec "$CLIENT" -c $@
fi

mwolson commented Nov 24, 2016

Here's a variation that ensures Emacs.app is launched, waits for it to be ready, and then uses emacsclient to open the file in a GUI frame and wait for edits to be completed.

#!/bin/sh

CLIENT=/Applications/Emacs.app/Contents/MacOS/bin/emacsclient

$(/usr/bin/osascript <<-END
  tell application ((path to applications folder as text) & "Emacs.app")
    activate
  end tell
END
)

while ! "$CLIENT" -e '"pass"' 2>&1 | grep '"pass"' > /dev/null; do
    sleep 0.3
done

if test "$#" -gt 0; then
    exec "$CLIENT" -c $@
fi
@railwaycat

This comment has been minimized.

Show comment
Hide comment
@railwaycat

railwaycat Nov 30, 2016

Hi @mwolson, thanks for the update. I believe your starter script works for Nextstep port (although I haven't verify that by myself), but it not works for Mac port. Because Emacs Mac port lacks multi-tty support, which means, emacsclient from mac port can only able to create tty frames but not GUI frames.

Owner

railwaycat commented Nov 30, 2016

Hi @mwolson, thanks for the update. I believe your starter script works for Nextstep port (although I haven't verify that by myself), but it not works for Mac port. Because Emacs Mac port lacks multi-tty support, which means, emacsclient from mac port can only able to create tty frames but not GUI frames.

@ivanbrennan

This comment has been minimized.

Show comment
Hide comment
@ivanbrennan

ivanbrennan Jan 26, 2017

@ptb Just curious, why do you use a sub-shell to initialize t in your script? Any reason not to use t="" instead?

Edit: After some digging, I see I was misinterpreting the parentheses and mistaking an array assignment for a sub-shell.

ivanbrennan commented Jan 26, 2017

@ptb Just curious, why do you use a sub-shell to initialize t in your script? Any reason not to use t="" instead?

Edit: After some digging, I see I was misinterpreting the parentheses and mistaking an array assignment for a sub-shell.

@achikin

This comment has been minimized.

Show comment
Hide comment
@achikin

achikin May 12, 2017

Do I need to start server in emacs config before running this? because emacsclient gives me the following error

emacsclient -c nginx.conf
emacsclient: can't find socket; have you started the server?
To start the server in Emacs, type "M-x server-start".

achikin commented May 12, 2017

Do I need to start server in emacs config before running this? because emacsclient gives me the following error

emacsclient -c nginx.conf
emacsclient: can't find socket; have you started the server?
To start the server in Emacs, type "M-x server-start".
@ivanbrennan

This comment has been minimized.

Show comment
Hide comment
@ivanbrennan

ivanbrennan Jun 5, 2017

I made some small modifications to the script @ptb provided. This variation is callable with or without file args, and will make emacs visible even if it was previously hidden.

The if [ $# -gt 0] block that handles file args is run in the background (see the closing fi &), but its contents run synchronously with respect to each other. That prevents deleting the tempfiles before they've been visited, with no need for sleep.

#!/bin/sh

emacs_app=/usr/local/opt/emacs-mac/Emacs.app

if [ ! -x $emacs_app ]; then
  echo "Emacs.app not found" >&2
  exit 1
fi

/usr/bin/osascript -e "tell application \"$emacs_app\" to activate" &

if [ $# -gt 0 ]; then
  tempfiles=()

  while IFS= read -r filename; do
    if [ ! -f "$filename" ]; then
      tempfiles+=("$filename")
      /usr/bin/touch "$filename"
    fi

    file=$(echo $(cd $(dirname "$filename") && pwd -P)/$(basename "$filename"))
    /usr/bin/osascript -e "tell application \"$emacs_app\" to open POSIX file \"$file\""
  done <<< "$(printf '%s\n' "$@")"

  for tempfile in "${tempfiles[@]}"; do
    [ ! -s "$tempfile" ] && /bin/rm "$tempfile"
  done
fi &

ivanbrennan commented Jun 5, 2017

I made some small modifications to the script @ptb provided. This variation is callable with or without file args, and will make emacs visible even if it was previously hidden.

The if [ $# -gt 0] block that handles file args is run in the background (see the closing fi &), but its contents run synchronously with respect to each other. That prevents deleting the tempfiles before they've been visited, with no need for sleep.

#!/bin/sh

emacs_app=/usr/local/opt/emacs-mac/Emacs.app

if [ ! -x $emacs_app ]; then
  echo "Emacs.app not found" >&2
  exit 1
fi

/usr/bin/osascript -e "tell application \"$emacs_app\" to activate" &

if [ $# -gt 0 ]; then
  tempfiles=()

  while IFS= read -r filename; do
    if [ ! -f "$filename" ]; then
      tempfiles+=("$filename")
      /usr/bin/touch "$filename"
    fi

    file=$(echo $(cd $(dirname "$filename") && pwd -P)/$(basename "$filename"))
    /usr/bin/osascript -e "tell application \"$emacs_app\" to open POSIX file \"$file\""
  done <<< "$(printf '%s\n' "$@")"

  for tempfile in "${tempfiles[@]}"; do
    [ ! -s "$tempfile" ] && /bin/rm "$tempfile"
  done
fi &
@lolh-linc

This comment has been minimized.

Show comment
Hide comment
@lolh-linc

lolh-linc Jun 13, 2017

This will create buffers of any number of existing or non-existing files with or without spaces in their names in a new instance of Emacs.app. emacs_starter file1 file2 file3 ...

#! /usr/bin/env perl
local $" = qq{" "};
my (@have, @havenot);
for (@ARGV) {-e && {push @have, $_} || push @havenot, $_}
system qq{open -a Emacs.app "@have" --args "@havenot"};

If you have an emacs server running, then typing emacsclient file1 file2 file3 ... will use the server.

This will create buffers of any number of existing or non-existing files with or without spaces in their names in a new instance of Emacs.app. emacs_starter file1 file2 file3 ...

#! /usr/bin/env perl
local $" = qq{" "};
my (@have, @havenot);
for (@ARGV) {-e && {push @have, $_} || push @havenot, $_}
system qq{open -a Emacs.app "@have" --args "@havenot"};

If you have an emacs server running, then typing emacsclient file1 file2 file3 ... will use the server.

@worace

This comment has been minimized.

Show comment
Hide comment
@worace

worace Aug 24, 2017

Anyone know if it's possible to use emacsclient with emacs-mac in a way that opens a new cocoa frame with the -c flag? I've started emacs server using a system plist and am able to connect with /usr/local/opt/emacs-mac/bin/emacsclient, but only in a terminal session.

Interestingly it looks like some people seem to have a bin directory inside of /usr/local/opt/emacs-mac/Emacs.app/Contents/MacOS but I do not.

ls -la /usr/local/opt/emacs-mac/Emacs.app/Contents/MacOS
total 24912
drwxr-xr-x  4 horace  admin       136 Aug 23 12:38 .
drwxr-xr-x  6 horace  admin       204 Aug 23 12:33 ..
-rwxr-xr-x  1 horace  admin  12747496 Aug 23 12:38 Emacs
-rwxr-xr-x  1 horace  admin      1650 Jul 26 21:44 Emacs.sh

If it's useful to anyone here's the plist I'm using to start the emacs daemon automatically on login:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>gnu.emacs.daemon</string>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/local/opt/emacs-mac/bin/emacs</string>
      <string>--daemon</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>ServiceDescription</key>
    <string>Gnu Emacs Daemon</string>
    <key>UserName</key>
    <string>worace</string>
  </dict>
</plist>

Then I have a couple utility functions for restarting it or stopping it as needed:

function emrestart {
    if pgrep "emacs.*daemon" > /dev/null
    then
        echo "killing emacs daemon process"
        $EMACS_BIN_DIR/emacsclient -e "(kill-emacs)"
    fi
  launchctl unload "$HOME/Library/LaunchAgents/emacsserver.plist" &&
  launchctl load "$HOME/Library/LaunchAgents/emacsserver.plist"
}

function emstop {
    if pgrep "emacs.*daemon" > /dev/null
    then
        echo "killing emacs daemon process"
        $EMACS_BIN_DIR/emacsclient -e "(kill-emacs)"
    fi
}

Would love to know if anyone has figured out the emacsclient thing though. That's the only thing I seem to be able to do with the default homebrew emacs setup that I haven't figured out with emacs-mac.

worace commented Aug 24, 2017

Anyone know if it's possible to use emacsclient with emacs-mac in a way that opens a new cocoa frame with the -c flag? I've started emacs server using a system plist and am able to connect with /usr/local/opt/emacs-mac/bin/emacsclient, but only in a terminal session.

Interestingly it looks like some people seem to have a bin directory inside of /usr/local/opt/emacs-mac/Emacs.app/Contents/MacOS but I do not.

ls -la /usr/local/opt/emacs-mac/Emacs.app/Contents/MacOS
total 24912
drwxr-xr-x  4 horace  admin       136 Aug 23 12:38 .
drwxr-xr-x  6 horace  admin       204 Aug 23 12:33 ..
-rwxr-xr-x  1 horace  admin  12747496 Aug 23 12:38 Emacs
-rwxr-xr-x  1 horace  admin      1650 Jul 26 21:44 Emacs.sh

If it's useful to anyone here's the plist I'm using to start the emacs daemon automatically on login:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>gnu.emacs.daemon</string>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/local/opt/emacs-mac/bin/emacs</string>
      <string>--daemon</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>ServiceDescription</key>
    <string>Gnu Emacs Daemon</string>
    <key>UserName</key>
    <string>worace</string>
  </dict>
</plist>

Then I have a couple utility functions for restarting it or stopping it as needed:

function emrestart {
    if pgrep "emacs.*daemon" > /dev/null
    then
        echo "killing emacs daemon process"
        $EMACS_BIN_DIR/emacsclient -e "(kill-emacs)"
    fi
  launchctl unload "$HOME/Library/LaunchAgents/emacsserver.plist" &&
  launchctl load "$HOME/Library/LaunchAgents/emacsserver.plist"
}

function emstop {
    if pgrep "emacs.*daemon" > /dev/null
    then
        echo "killing emacs daemon process"
        $EMACS_BIN_DIR/emacsclient -e "(kill-emacs)"
    fi
}

Would love to know if anyone has figured out the emacsclient thing though. That's the only thing I seem to be able to do with the default homebrew emacs setup that I haven't figured out with emacs-mac.

@tonylotts

This comment has been minimized.

Show comment
Hide comment
@tonylotts

tonylotts Sep 14, 2017

@worace If you start the server in a running GUI Emacs instance, emacsclient will connect to it.
It would be ideal to be able to start Emacs as a daemon process, and have GUI Emacs connect to it, but unfortunately I haven't gotten that to work.

@worace If you start the server in a running GUI Emacs instance, emacsclient will connect to it.
It would be ideal to be able to start Emacs as a daemon process, and have GUI Emacs connect to it, but unfortunately I haven't gotten that to work.

@johnhamelink

This comment has been minimized.

Show comment
Hide comment
@johnhamelink

johnhamelink Sep 19, 2017

@tonylotts do you know if it's possible to have the GUI emacs client connect to an already existing emacs daemon instance?

@tonylotts do you know if it's possible to have the GUI emacs client connect to an already existing emacs daemon instance?

@ChillingHsu

This comment has been minimized.

Show comment
Hide comment
@ChillingHsu

ChillingHsu May 26, 2018

@worace @johnhamelink
My solution to run GUI emacs through emacsclient is checking Emacs.app whether running. The only configuration you should do is add (server-start) to your emacs init.el, you can see details at my gist

#!/bin/bash
BG_RED=`tput setaf 1`
BG_GREEN=`tput setaf 2`
BOLD=`tput bold`
RESET=`tput sgr0`

EMACS='/Applications/Emacs.app'
EMACS_CLIENT='/Applications/Emacs.app/Contents/MacOS/bin/emacsclient'

DEFAULT_EVAL='(switch-to-buffer "*scratch*")'
DEFAULT_ARGS="-e"
NO_WAIT='-n'

function run_client(){
    if [ $# -ne 0 ]
    then
        ${EMACS_CLIENT} ${NO_WAIT} $@
    else
        ${EMACS_CLIENT} ${NO_WAIT} ${DEFAULT_ARGS} "${DEFAULT_EVAL}" &> /dev/null
    fi
}

echo -e "Checking Emacs server status...\c"
if pgrep Emacs &> /dev/null
then
    echo "${BOLD}${BG_GREEN}Active${RESET}"
    echo -e "Connecting...\c"
    run_client $*
    echo "${BOLD}${BG_GREEN}DONE${RESET}"
else
    echo "${BOLD}${BG_RED}Inactive${RESET}"
    echo -e "Emacs server is starting...\c"
    open -a ${EMACS}
    echo "${BOLD}${BG_GREEN}DONE${RESET}"

    echo -e "Trying connecting...\c"
    until run_client $* &> /dev/null;[ $? -eq 0 ]
    do
        sleep 1
    done
    echo "${BOLD}${BG_GREEN}DONE${RESET}"
fi

@worace @johnhamelink
My solution to run GUI emacs through emacsclient is checking Emacs.app whether running. The only configuration you should do is add (server-start) to your emacs init.el, you can see details at my gist

#!/bin/bash
BG_RED=`tput setaf 1`
BG_GREEN=`tput setaf 2`
BOLD=`tput bold`
RESET=`tput sgr0`

EMACS='/Applications/Emacs.app'
EMACS_CLIENT='/Applications/Emacs.app/Contents/MacOS/bin/emacsclient'

DEFAULT_EVAL='(switch-to-buffer "*scratch*")'
DEFAULT_ARGS="-e"
NO_WAIT='-n'

function run_client(){
    if [ $# -ne 0 ]
    then
        ${EMACS_CLIENT} ${NO_WAIT} $@
    else
        ${EMACS_CLIENT} ${NO_WAIT} ${DEFAULT_ARGS} "${DEFAULT_EVAL}" &> /dev/null
    fi
}

echo -e "Checking Emacs server status...\c"
if pgrep Emacs &> /dev/null
then
    echo "${BOLD}${BG_GREEN}Active${RESET}"
    echo -e "Connecting...\c"
    run_client $*
    echo "${BOLD}${BG_GREEN}DONE${RESET}"
else
    echo "${BOLD}${BG_RED}Inactive${RESET}"
    echo -e "Emacs server is starting...\c"
    open -a ${EMACS}
    echo "${BOLD}${BG_GREEN}DONE${RESET}"

    echo -e "Trying connecting...\c"
    until run_client $* &> /dev/null;[ $? -eq 0 ]
    do
        sleep 1
    done
    echo "${BOLD}${BG_GREEN}DONE${RESET}"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment