18

Is there any way to set +x bit on script while creating?

For example I run:

vim -some_option_to_make_file_executable script.sh

and after saving I can run file without any additional movings.

ps. I can run chmod from vim or even from console itself, but this is a little annoying, cause vim suggests to reload file. Also it's annoying to type chmod command every time. pps. It would be great to make it depending on file extension (I don't need executable .txt :-) )

rush
  • 27,403

5 Answers5

27

I don't recall where I found this, but I use the following in my ~/.vimrc

" Set scripts to be executable from the shell
au BufWritePost * if getline(1) =~ "^#!" | if getline(1) =~ "/bin/" | silent !chmod +x <afile> | endif | endif

The command automatically sets the executable bit if the first line starts with "#!" or contains "/bin/".

tonymac
  • 386
  • 1
    Wow, that's great thing. Btw, seems you can join two if's into one if getline(1) =~ "^#!/bin/". Anyway that's amazing. Thank you. – rush Jun 04 '12 at 13:39
  • This is not a "or". I prefer to use just first condition, au BufWritePost * if getline(1) =~ "^#!" | silent !chmod +x % | endif – vault Feb 17 '16 at 14:39
  • 1
    @rush The reasons for the double if statements is to to catch lines where the the /bin doesn't immediately follow the shebang, like #!/usr/bin/env. A way around that is of course to use a wildcard: getline(1) =~ "^#!.*/bin/". – Harald Nordgren Apr 02 '16 at 14:47
  • 2
    Neat trick but I get the following every time I save: written/bin/bash: endif: command not found /bin/bash: endif: command not found – StevieD Feb 10 '17 at 21:36
  • 3
    this fixes it: au BufWritePost * if getline(1) =~ "^#!" | if getline(1) =~ "/bin/" | silent execute "!chmod a+x <afile>" | endif | endif – StevieD Feb 10 '17 at 21:44
4

I found this script at http://vim.wikia.com. Not a perfect solution, but a acceptable one, I think.

function! SetExecutableBit()
  let fname = expand("%:p")
  checktime
  execute "au FileChangedShell " . fname . " :echo"
  silent !chmod a+x %
  checktime
  execute "au! FileChangedShell " . fname
endfunction
command! Xbit call SetExecutableBit()

You can now set the execute bit with the command :Xbit. All credit to Max Ischenko at vim.wikia.com

nsg
  • 1,216
1

I wrote myself a version of this that is a bit more selective. It only makes the file executable when writing a new file, and only if it starts with #!. This version also avoids executing an external command, doing everything in pure vimscript.

" automatically make script files executable when writing for the first time
function! NewScriptExec() abort
    " check if this is a new file which starts with a shebang
    if exists('s:new_file') && getline(1)[0:1] == '#!'
        " based on https://stackoverflow.com/a/57539332/370695
        let l:file = expand('%')
        let l:old_perm = getfperm(l:file)
        " set the exec bit everywhere the read bit is set
        let l:new_perm = substitute(l:old_perm, '\v(r.)-', '\1x', 'g')
        if (l:old_perm != l:new_perm)
            call setfperm(l:file, l:new_perm)
        endif
        unlet s:new_file
    endif
endfunction

augroup new_script_exec autocmd! autocmd BufNewFile * let s:new_file = 1 autocmd BufWritePost * call NewScriptExec() augroup END

ryanc
  • 115
0

I use this in MacVim Custom Version 8.0.648 (134)

" if file is executable just exit

au BufWritePost *.sh if FileExecutable("%") | if getline(1) =~ "^#!" | silent !chmod u+x % | endif | endif

" Determines if file is already executable 

function! FileExecutable(fname)

    execute "silent! ! test -x" a:fname
    return v:shell_error

endfunction
Jaleks
  • 2,579
TonyB
  • 1
0

tonymac's answer stopped working for me at some point (with VIM 7.4), giving me the same problem as @StevieD. Modifying it fixed the problem:

au BufWritePost * if getline(1) =~ "^#!" | if getline(1) =~ "/bin/" | silent execute "!chmod +x <afile>" | endif | endif

I found the answer from https://bbs.archlinux.org/viewtopic.php?id=126304, although @StevieD also gave the same answer.

Jonathan
  • 775