1

Suppose, we have a perl file where we want to swap shebangs located in the first line:

"#!/usr/bin/perl -d" <-> "#!/usr/bin/env perl"

The first one runs the file with debugger enabled, and the second one - without the debugger.

I want to change the first line in the file /home/user/run.pl. If it is #!/usr/bin/perl -d, change it to #!/usr/bin/env perl. Otherwise, change to #!/usr/bin/perl -d.

I created the following elisp function for emacs that does the job:

(defun swap-debugging ()
  "Change the shebang of script to enable/disable debugging"
  (interactive)
  (find-file "/home/user/run.pl")
  (let ((line (thing-at-point 'line t)))
    ; remove \n
    (setq line (substring line 0 -1))
    (delete-region 1 (line-end-position))
    (if (string-match-p "^#!/usr/bin/env" line)
      ; set debug if there was no debug
      (insert "#!/usr/bin/perl -d")
      ; set no debug if there was debug
      (insert "#!/usr/bin/env perl"))
    (save-buffer)
    (kill-this-buffer)))

How can I create the same function in Vim?

3
  • What did you try?
    – romainl
    Commented Nov 27, 2023 at 17:24
  • 1
    I doubt many people on this site are literate in elisp. Might be easier to explain what you want to achieve in plain English.
    – Friedrich
    Commented Nov 27, 2023 at 18:54
  • 1
    @Friedrich I want to change the first line in the file /home/user/run.pl. If it is "#!/usr/bin/perl -d", change it to "#!/usr/bin/env perl". Otherwise, change to "#!/usr/bin/perl -d".
    – user4035
    Commented Nov 28, 2023 at 7:15

3 Answers 3

2

To toggle a file's shebang, I'd use the following quick and dirty one-liner:

:1s@^#!/usr/bin/\zs.*@\=submatch(0)=='perl -d'?'env perl':'perl -d'

In English, that's: in line 1, substitute the characters after #!/usr/bin/ with env perl if they are already perl -d, with perl -d otherwise.

For reference, see :help :substitute, :help /\zs and :help :s\=.

To always change the file /home/user/run.pl you could use the following function that opens the file before and writes and closes it

function SwapDebugging()
    new /home/user/run.pl
    1substitute@^#!/usr/bin/\zs.*@\=submatch(0)=='perl -d'?'env perl':'perl -d'
    write
    close
endfunction

and call it with :call SwapDebugging(). This will open the file, change it as explained above, write and close it again.

Having said that, changing a script's shebang to toggle debugging is a clever solution to a problem that shouldn't exist in the first place. To run your file with debugging turned on, just do :!perl -d %. You could :set makeprg=perl\ -d\ % and run with :mak if you really want to save a few keystrokes.

But there's no need to ever look at the shebang, much less change it. If anything, these changes will be a distraction in version control.

3
  • "changing a script's shebang to toggle debugging is a clever solution to a problem that shouldn't exist in the first place" - this script is launched from another script that set multiple environment variables. And changing shebang of the child script was the only solution I found.
    – user4035
    Commented Nov 28, 2023 at 7:18
  • Is it possible to change it in a certain file: open file, change the shebang, then - close file? Like I do in Emacs.
    – user4035
    Commented Nov 28, 2023 at 7:19
  • @user4035 well, e.g. :new /home/user/run.pl opens, :wq writes and closes. I'll update my answer later this day. What you write about your scripts sounds fishy to me but this is not the place to debug perl (nor am I the person who should be doing it). And why change editors mid-flight? If you're happy and productive with Emacs, stick with it.
    – Friedrich
    Commented Nov 28, 2023 at 7:30
2

Here's a POSIX-portable version that only relies on ed and can be shoved into a shell script:

printf %s\\n '1s/ -d' wq | ed -s $file

This uses the shorthand for s that only requires the delimited pattern when you want to delete it; you could obviously construct a fuller substitution or even a series of commands. The latter is better kept in a file (newlines delimit commands) and run with <edit-commands ed -s $file

4
  • what is that supposed to be doing? It seems like this should just delete the first line, but it doesn't work. also it might be easier to just use 1d directly? Or use sed instead Commented Dec 1, 2023 at 9:31
  • Sed is not suited for these tasks (see my comment on the sed answer). It should be equivalent to :1s/ -d// | wq in Vim, hence deleting the -d flag.
    – D. Ben Knoble
    Commented Dec 1, 2023 at 22:47
  • @ChristianBrabandt if it’s not working I will need to check; I may need to provide the extra s delimiters
    – D. Ben Knoble
    Commented Dec 1, 2023 at 22:48
  • I would argue, sed is exactly the command needed to use here. And yes I know about the problem with the -i argument. Commented Dec 2, 2023 at 11:41
2

"#!/usr/bin/perl -d" <-> "#!/usr/bin/env perl"

First off, you do not want to remove the '-d', in fact you want to change the whole line. If you want to do that not for one file but for many files then vi is probably not the right tool to do the job. sed is perhaps better suited for that purpose, for instance (ksh - if you use another shell change the print -u2 - to whatever creates output on stderr on your system):

for file in [... list of files ...] ; do
     if sed '1 s:#!/usr/bin/perl -d:#!/usr/bin/env perl:' "$file" > "${file}.tmp" ; then
          mv "${file}.tmp" "$file"
     else
          print -u2 - "Failed to process $file"
     fi
done

If you want to do it in vi itself you use ":" to get into command mode and then type the same command as with sed:

 :1 s:#!/usr/bin/perl -d:#!/usr/bin/env perl:

The "1" in front makes sure only the first line is changed (if any). You can also create a keyboard shortcut for that by using "map" in your .exrc file.

It is also possible to fine-tune the regex to allow for additional white space and the like, which is intentionally left out as not being the focus of your question.

5
  • Both sed and Vim's :substitute allow arbitrary separators to prevent leaning toothpicks. Makes it easier to write and easier to read.
    – Friedrich
    Commented Nov 30, 2023 at 13:28
  • @Friedrich: true! I changed my examples.
    – bakunin
    Commented Nov 30, 2023 at 13:30
  • Sed is a stream editor, not a file editor, though at least you didn’t use -i :)
    – D. Ben Knoble
    Commented Nov 30, 2023 at 15:47
  • @D.BenKnoble: true - this is why I suggested it for mass-changing many files and gave an alternative for one file in an editor buffer. "-i" is - IMHO - an abomination and I said so on many occasions, i.e. here: unix.com/unix-for-beginners-questions-and-answers/…
    – bakunin
    Commented Nov 30, 2023 at 19:28
  • I think my point was that you could as well us ed here…
    – D. Ben Knoble
    Commented Dec 1, 2023 at 22:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.