Bash shadow a command - function with same name as command [duplicate]

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
5
down vote

favorite













This question already has an answer here:



  • Running an executable in PATH with the same name as an existing function

    3 answers



I have a function in my .bashrc to automatically sudo to open files that aren't writable by me:



vim() 
if [ -w "$1" ]; then
vim "$1"
else
sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
fi



When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.



From this answer I see there are a few options, all of which I've tried. One actually works:



'vim' "$1" #fails
vim "$1" #fails
command vim "$1" #Works!


Why do the other options not work as I would expect them to?



(I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)







share|improve this question











marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27


This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.


















    up vote
    5
    down vote

    favorite













    This question already has an answer here:



    • Running an executable in PATH with the same name as an existing function

      3 answers



    I have a function in my .bashrc to automatically sudo to open files that aren't writable by me:



    vim() 
    if [ -w "$1" ]; then
    vim "$1"
    else
    sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
    fi



    When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.



    From this answer I see there are a few options, all of which I've tried. One actually works:



    'vim' "$1" #fails
    vim "$1" #fails
    command vim "$1" #Works!


    Why do the other options not work as I would expect them to?



    (I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)







    share|improve this question











    marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27


    This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
















      up vote
      5
      down vote

      favorite









      up vote
      5
      down vote

      favorite












      This question already has an answer here:



      • Running an executable in PATH with the same name as an existing function

        3 answers



      I have a function in my .bashrc to automatically sudo to open files that aren't writable by me:



      vim() 
      if [ -w "$1" ]; then
      vim "$1"
      else
      sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
      fi



      When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.



      From this answer I see there are a few options, all of which I've tried. One actually works:



      'vim' "$1" #fails
      vim "$1" #fails
      command vim "$1" #Works!


      Why do the other options not work as I would expect them to?



      (I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)







      share|improve this question












      This question already has an answer here:



      • Running an executable in PATH with the same name as an existing function

        3 answers



      I have a function in my .bashrc to automatically sudo to open files that aren't writable by me:



      vim() 
      if [ -w "$1" ]; then
      vim "$1"
      else
      sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
      fi



      When the file needs sudo, it works fine. When it doesn't, it recursively calls this function and uses 100% of 1 CPU until I C-C.



      From this answer I see there are a few options, all of which I've tried. One actually works:



      'vim' "$1" #fails
      vim "$1" #fails
      command vim "$1" #Works!


      Why do the other options not work as I would expect them to?



      (I know this is a duplicate, but it was very hard to find my answer on SO/SE with the current question titles, so I wanted to post a question with a title that I and others could've found by google searching)





      This question already has an answer here:



      • Running an executable in PATH with the same name as an existing function

        3 answers









      share|improve this question










      share|improve this question




      share|improve this question









      asked Jul 10 at 19:18









      jeremysprofile

      20910




      20910




      marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27


      This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.






      marked as duplicate by muru, G-Man, Anthony Geoghegan, Jeff Schaller, schily Jul 11 at 12:27


      This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.






















          3 Answers
          3






          active

          oldest

          votes

















          up vote
          6
          down vote



          accepted










          The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do



          alias v='sudo vim'
          v x.txt
          #automatically expands "v" to "sudo vim" and then does the rest of the command
          # v x.txt ==> sudo vim x.txt


          Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt won't expand into sudo vimim x.txt using the alias above) in a command to expand properly.



          However, expansion never happens to things in single quotes: echo '$USER' will print out a literal $USER and not what the variable stands for. Also, bash expands x to be x (mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.



          alias ls='ls -la'
          ls foo.txt #will expand into ls -la foo.txt
          ls foo.txt #will expand into ls foo.txt
          'ls' foo.txt #same as above
          'ls foo.txt' #same as above


          However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.



          ls () 
          echo "not an alias"

          alias ls='echo "an alias"'

          ls foo.txt #will echo "an alias"
          ls foo.txt #will echo "not an alias"
          'ls' foo.txt #will echo "not an alias"
          command ls foo.txt #will actually run `ls`





          share|improve this answer




























            up vote
            1
            down vote













            If you have the function call the full path to the vim binary, you won't get the recursive loop. Also, using the -H option to sudo sets $HOME.



            vim() 
            if [ -w "$1" ]; then
            command vim "$1"
            else
            sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
            fi



            Thanks. I hadn't thought about this, but now I have it in my .bashrc, too.




            EDITED:
            updated calling command vim and sudo env instead of sudo -h. this way there is no loop to stay in.






            share|improve this answer



















            • 1




              sudo -H does not work here (I tried it). It sets $HOME equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
              – jeremysprofile
              Jul 10 at 22:14






            • 2




              I would suggest command vim as a more generic (PATH-respecting) approach, rather than /usr/bin/vim.
              – Charles Duffy
              Jul 10 at 23:38










            • @CharlesDuffy thanks for the tip. I have added that to the answer.
              – Tim Kennedy
              Jul 11 at 1:11










            • @jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
              – Tim Kennedy
              Jul 11 at 1:14

















            up vote
            0
            down vote














            Why do the other options not work as I would expect them to?




            Because there are some details in the way a command line gets executed.



            The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:




            1. The command line is split on metacharacters (mostly):




              metacharacters

              A character that, when unquoted, separates words. One of the following:

              | & ; ( ) < > space tab newline





            2. If the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:



              $ alias ls='ls -la'
              $ ls dir # will expand to `ls -la dir`


              Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.




            3. If the (resulting) first word is an expansion (like a $var), it is replaced (and subject to IFS word splitting (and globing) if not quoted):



              $ var='echo -e '
              $ $var test 'twords' # will expand to `echo -e test words`
              test words



            4. The resulting first word after above expansions (after optional variable assignments) will be searched in this order:



              • special builtins (only in POSIX mode)

              • functions

              • builtins

              • hashed commands (read help hash)

              • external commands (searched in $PATH dirs)

              The first match found will be executed.



            The order may be changed by (for test):



            • To bypass just alias, use test or any other kind of expansion.

            • To ignore functions and aliases, use command test.

            • To execute a builtin, use builtin test.

            • To execute an external application, use an explicit path: /bin/test.

            So, a function defined as:



            $ ls() ls; 


            Will be called in an infinite loop. As also an script which calls the same first word. Something like:



            #!/bin/bash
            $0 "$@"


            Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).



            The order will be shown by running type -a:



            $ test() echo test function; 
            $ alias test=test
            $ type -a
            test is aliased to `test'
            test is a function
            test ()

            echo test function

            test is a shell builtin
            test is /usr/bin/test


            The function (as defined in the question) will only bypass the alias with vim but not the function. To bypass alias and functions, use command. This function should do what you need:



            vim() 
            if [ -w "$1" ]; then
            command vim "$1"
            else
            sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
            fi






            share|improve this answer























            • This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
              – Charles Duffy
              Jul 11 at 12:07











            • Consider instead sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1" -- then the ~ is evaluated by the parent shell before sudo even starts, and you're passing the filename out-of-band from the script text.
              – Charles Duffy
              Jul 11 at 12:09











            • (er, should have been "with touch ..." rather than "named touch ..." above).
              – Charles Duffy
              Jul 11 at 12:14











            • @CharlesDuffy Sure, that's correct, thanks.
              – Isaac
              Jul 11 at 13:44

















            3 Answers
            3






            active

            oldest

            votes








            3 Answers
            3






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            6
            down vote



            accepted










            The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do



            alias v='sudo vim'
            v x.txt
            #automatically expands "v" to "sudo vim" and then does the rest of the command
            # v x.txt ==> sudo vim x.txt


            Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt won't expand into sudo vimim x.txt using the alias above) in a command to expand properly.



            However, expansion never happens to things in single quotes: echo '$USER' will print out a literal $USER and not what the variable stands for. Also, bash expands x to be x (mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.



            alias ls='ls -la'
            ls foo.txt #will expand into ls -la foo.txt
            ls foo.txt #will expand into ls foo.txt
            'ls' foo.txt #same as above
            'ls foo.txt' #same as above


            However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.



            ls () 
            echo "not an alias"

            alias ls='echo "an alias"'

            ls foo.txt #will echo "an alias"
            ls foo.txt #will echo "not an alias"
            'ls' foo.txt #will echo "not an alias"
            command ls foo.txt #will actually run `ls`





            share|improve this answer

























              up vote
              6
              down vote



              accepted










              The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do



              alias v='sudo vim'
              v x.txt
              #automatically expands "v" to "sudo vim" and then does the rest of the command
              # v x.txt ==> sudo vim x.txt


              Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt won't expand into sudo vimim x.txt using the alias above) in a command to expand properly.



              However, expansion never happens to things in single quotes: echo '$USER' will print out a literal $USER and not what the variable stands for. Also, bash expands x to be x (mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.



              alias ls='ls -la'
              ls foo.txt #will expand into ls -la foo.txt
              ls foo.txt #will expand into ls foo.txt
              'ls' foo.txt #same as above
              'ls foo.txt' #same as above


              However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.



              ls () 
              echo "not an alias"

              alias ls='echo "an alias"'

              ls foo.txt #will echo "an alias"
              ls foo.txt #will echo "not an alias"
              'ls' foo.txt #will echo "not an alias"
              command ls foo.txt #will actually run `ls`





              share|improve this answer























                up vote
                6
                down vote



                accepted







                up vote
                6
                down vote



                accepted






                The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do



                alias v='sudo vim'
                v x.txt
                #automatically expands "v" to "sudo vim" and then does the rest of the command
                # v x.txt ==> sudo vim x.txt


                Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt won't expand into sudo vimim x.txt using the alias above) in a command to expand properly.



                However, expansion never happens to things in single quotes: echo '$USER' will print out a literal $USER and not what the variable stands for. Also, bash expands x to be x (mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.



                alias ls='ls -la'
                ls foo.txt #will expand into ls -la foo.txt
                ls foo.txt #will expand into ls foo.txt
                'ls' foo.txt #same as above
                'ls foo.txt' #same as above


                However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.



                ls () 
                echo "not an alias"

                alias ls='echo "an alias"'

                ls foo.txt #will echo "an alias"
                ls foo.txt #will echo "not an alias"
                'ls' foo.txt #will echo "not an alias"
                command ls foo.txt #will actually run `ls`





                share|improve this answer













                The problem is that the first two options are designed only to deal with aliases. They are not special redirecting operators that can sense if you have a function or command with the same name. All they do is prevent expansion, which is what an alias tries to do



                alias v='sudo vim'
                v x.txt
                #automatically expands "v" to "sudo vim" and then does the rest of the command
                # v x.txt ==> sudo vim x.txt


                Bash just tries to expand the first word of a command using the list of aliases it knows about (which you can get with alias). Aliases don't take arguments, and can only be the first word (space-separated; vim x.txt won't expand into sudo vimim x.txt using the alias above) in a command to expand properly.



                However, expansion never happens to things in single quotes: echo '$USER' will print out a literal $USER and not what the variable stands for. Also, bash expands x to be x (mostly). These aren't extra added-in ways specifically to escape an alias, they're just part of how bash expansion was written. Thus, you can use these methods to let an alias shadow a command and still have access to the actual command.



                alias ls='ls -la'
                ls foo.txt #will expand into ls -la foo.txt
                ls foo.txt #will expand into ls foo.txt
                'ls' foo.txt #same as above
                'ls foo.txt' #same as above


                However, they don't stop functions. A function doesn't need expansion to work, it is called with its name.



                ls () 
                echo "not an alias"

                alias ls='echo "an alias"'

                ls foo.txt #will echo "an alias"
                ls foo.txt #will echo "not an alias"
                'ls' foo.txt #will echo "not an alias"
                command ls foo.txt #will actually run `ls`






                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Jul 10 at 19:18









                jeremysprofile

                20910




                20910






















                    up vote
                    1
                    down vote













                    If you have the function call the full path to the vim binary, you won't get the recursive loop. Also, using the -H option to sudo sets $HOME.



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
                    fi



                    Thanks. I hadn't thought about this, but now I have it in my .bashrc, too.




                    EDITED:
                    updated calling command vim and sudo env instead of sudo -h. this way there is no loop to stay in.






                    share|improve this answer



















                    • 1




                      sudo -H does not work here (I tried it). It sets $HOME equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
                      – jeremysprofile
                      Jul 10 at 22:14






                    • 2




                      I would suggest command vim as a more generic (PATH-respecting) approach, rather than /usr/bin/vim.
                      – Charles Duffy
                      Jul 10 at 23:38










                    • @CharlesDuffy thanks for the tip. I have added that to the answer.
                      – Tim Kennedy
                      Jul 11 at 1:11










                    • @jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
                      – Tim Kennedy
                      Jul 11 at 1:14














                    up vote
                    1
                    down vote













                    If you have the function call the full path to the vim binary, you won't get the recursive loop. Also, using the -H option to sudo sets $HOME.



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
                    fi



                    Thanks. I hadn't thought about this, but now I have it in my .bashrc, too.




                    EDITED:
                    updated calling command vim and sudo env instead of sudo -h. this way there is no loop to stay in.






                    share|improve this answer



















                    • 1




                      sudo -H does not work here (I tried it). It sets $HOME equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
                      – jeremysprofile
                      Jul 10 at 22:14






                    • 2




                      I would suggest command vim as a more generic (PATH-respecting) approach, rather than /usr/bin/vim.
                      – Charles Duffy
                      Jul 10 at 23:38










                    • @CharlesDuffy thanks for the tip. I have added that to the answer.
                      – Tim Kennedy
                      Jul 11 at 1:11










                    • @jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
                      – Tim Kennedy
                      Jul 11 at 1:14












                    up vote
                    1
                    down vote










                    up vote
                    1
                    down vote









                    If you have the function call the full path to the vim binary, you won't get the recursive loop. Also, using the -H option to sudo sets $HOME.



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
                    fi



                    Thanks. I hadn't thought about this, but now I have it in my .bashrc, too.




                    EDITED:
                    updated calling command vim and sudo env instead of sudo -h. this way there is no loop to stay in.






                    share|improve this answer















                    If you have the function call the full path to the vim binary, you won't get the recursive loop. Also, using the -H option to sudo sets $HOME.



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo env HOME="$HOME" vim -u ~/.vimrc "$1"
                    fi



                    Thanks. I hadn't thought about this, but now I have it in my .bashrc, too.




                    EDITED:
                    updated calling command vim and sudo env instead of sudo -h. this way there is no loop to stay in.







                    share|improve this answer















                    share|improve this answer



                    share|improve this answer








                    edited Jul 11 at 1:09


























                    answered Jul 10 at 21:55









                    Tim Kennedy

                    13.1k22848




                    13.1k22848







                    • 1




                      sudo -H does not work here (I tried it). It sets $HOME equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
                      – jeremysprofile
                      Jul 10 at 22:14






                    • 2




                      I would suggest command vim as a more generic (PATH-respecting) approach, rather than /usr/bin/vim.
                      – Charles Duffy
                      Jul 10 at 23:38










                    • @CharlesDuffy thanks for the tip. I have added that to the answer.
                      – Tim Kennedy
                      Jul 11 at 1:11










                    • @jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
                      – Tim Kennedy
                      Jul 11 at 1:14












                    • 1




                      sudo -H does not work here (I tried it). It sets $HOME equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
                      – jeremysprofile
                      Jul 10 at 22:14






                    • 2




                      I would suggest command vim as a more generic (PATH-respecting) approach, rather than /usr/bin/vim.
                      – Charles Duffy
                      Jul 10 at 23:38










                    • @CharlesDuffy thanks for the tip. I have added that to the answer.
                      – Tim Kennedy
                      Jul 11 at 1:11










                    • @jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
                      – Tim Kennedy
                      Jul 11 at 1:14







                    1




                    1




                    sudo -H does not work here (I tried it). It sets $HOME equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
                    – jeremysprofile
                    Jul 10 at 22:14




                    sudo -H does not work here (I tried it). It sets $HOME equal to the new user's home, not the home of the user calling sudo. Also, you might like my .vimrc function to display a message if sudo triggered
                    – jeremysprofile
                    Jul 10 at 22:14




                    2




                    2




                    I would suggest command vim as a more generic (PATH-respecting) approach, rather than /usr/bin/vim.
                    – Charles Duffy
                    Jul 10 at 23:38




                    I would suggest command vim as a more generic (PATH-respecting) approach, rather than /usr/bin/vim.
                    – Charles Duffy
                    Jul 10 at 23:38












                    @CharlesDuffy thanks for the tip. I have added that to the answer.
                    – Tim Kennedy
                    Jul 11 at 1:11




                    @CharlesDuffy thanks for the tip. I have added that to the answer.
                    – Tim Kennedy
                    Jul 11 at 1:11












                    @jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
                    – Tim Kennedy
                    Jul 11 at 1:14




                    @jeremysprofile that is a pretty cool .vimrc function. thanks for sharing it.
                    – Tim Kennedy
                    Jul 11 at 1:14










                    up vote
                    0
                    down vote














                    Why do the other options not work as I would expect them to?




                    Because there are some details in the way a command line gets executed.



                    The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:




                    1. The command line is split on metacharacters (mostly):




                      metacharacters

                      A character that, when unquoted, separates words. One of the following:

                      | & ; ( ) < > space tab newline





                    2. If the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:



                      $ alias ls='ls -la'
                      $ ls dir # will expand to `ls -la dir`


                      Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.




                    3. If the (resulting) first word is an expansion (like a $var), it is replaced (and subject to IFS word splitting (and globing) if not quoted):



                      $ var='echo -e '
                      $ $var test 'twords' # will expand to `echo -e test words`
                      test words



                    4. The resulting first word after above expansions (after optional variable assignments) will be searched in this order:



                      • special builtins (only in POSIX mode)

                      • functions

                      • builtins

                      • hashed commands (read help hash)

                      • external commands (searched in $PATH dirs)

                      The first match found will be executed.



                    The order may be changed by (for test):



                    • To bypass just alias, use test or any other kind of expansion.

                    • To ignore functions and aliases, use command test.

                    • To execute a builtin, use builtin test.

                    • To execute an external application, use an explicit path: /bin/test.

                    So, a function defined as:



                    $ ls() ls; 


                    Will be called in an infinite loop. As also an script which calls the same first word. Something like:



                    #!/bin/bash
                    $0 "$@"


                    Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).



                    The order will be shown by running type -a:



                    $ test() echo test function; 
                    $ alias test=test
                    $ type -a
                    test is aliased to `test'
                    test is a function
                    test ()

                    echo test function

                    test is a shell builtin
                    test is /usr/bin/test


                    The function (as defined in the question) will only bypass the alias with vim but not the function. To bypass alias and functions, use command. This function should do what you need:



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
                    fi






                    share|improve this answer























                    • This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
                      – Charles Duffy
                      Jul 11 at 12:07











                    • Consider instead sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1" -- then the ~ is evaluated by the parent shell before sudo even starts, and you're passing the filename out-of-band from the script text.
                      – Charles Duffy
                      Jul 11 at 12:09











                    • (er, should have been "with touch ..." rather than "named touch ..." above).
                      – Charles Duffy
                      Jul 11 at 12:14











                    • @CharlesDuffy Sure, that's correct, thanks.
                      – Isaac
                      Jul 11 at 13:44














                    up vote
                    0
                    down vote














                    Why do the other options not work as I would expect them to?




                    Because there are some details in the way a command line gets executed.



                    The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:




                    1. The command line is split on metacharacters (mostly):




                      metacharacters

                      A character that, when unquoted, separates words. One of the following:

                      | & ; ( ) < > space tab newline





                    2. If the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:



                      $ alias ls='ls -la'
                      $ ls dir # will expand to `ls -la dir`


                      Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.




                    3. If the (resulting) first word is an expansion (like a $var), it is replaced (and subject to IFS word splitting (and globing) if not quoted):



                      $ var='echo -e '
                      $ $var test 'twords' # will expand to `echo -e test words`
                      test words



                    4. The resulting first word after above expansions (after optional variable assignments) will be searched in this order:



                      • special builtins (only in POSIX mode)

                      • functions

                      • builtins

                      • hashed commands (read help hash)

                      • external commands (searched in $PATH dirs)

                      The first match found will be executed.



                    The order may be changed by (for test):



                    • To bypass just alias, use test or any other kind of expansion.

                    • To ignore functions and aliases, use command test.

                    • To execute a builtin, use builtin test.

                    • To execute an external application, use an explicit path: /bin/test.

                    So, a function defined as:



                    $ ls() ls; 


                    Will be called in an infinite loop. As also an script which calls the same first word. Something like:



                    #!/bin/bash
                    $0 "$@"


                    Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).



                    The order will be shown by running type -a:



                    $ test() echo test function; 
                    $ alias test=test
                    $ type -a
                    test is aliased to `test'
                    test is a function
                    test ()

                    echo test function

                    test is a shell builtin
                    test is /usr/bin/test


                    The function (as defined in the question) will only bypass the alias with vim but not the function. To bypass alias and functions, use command. This function should do what you need:



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
                    fi






                    share|improve this answer























                    • This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
                      – Charles Duffy
                      Jul 11 at 12:07











                    • Consider instead sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1" -- then the ~ is evaluated by the parent shell before sudo even starts, and you're passing the filename out-of-band from the script text.
                      – Charles Duffy
                      Jul 11 at 12:09











                    • (er, should have been "with touch ..." rather than "named touch ..." above).
                      – Charles Duffy
                      Jul 11 at 12:14











                    • @CharlesDuffy Sure, that's correct, thanks.
                      – Isaac
                      Jul 11 at 13:44












                    up vote
                    0
                    down vote










                    up vote
                    0
                    down vote










                    Why do the other options not work as I would expect them to?




                    Because there are some details in the way a command line gets executed.



                    The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:




                    1. The command line is split on metacharacters (mostly):




                      metacharacters

                      A character that, when unquoted, separates words. One of the following:

                      | & ; ( ) < > space tab newline





                    2. If the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:



                      $ alias ls='ls -la'
                      $ ls dir # will expand to `ls -la dir`


                      Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.




                    3. If the (resulting) first word is an expansion (like a $var), it is replaced (and subject to IFS word splitting (and globing) if not quoted):



                      $ var='echo -e '
                      $ $var test 'twords' # will expand to `echo -e test words`
                      test words



                    4. The resulting first word after above expansions (after optional variable assignments) will be searched in this order:



                      • special builtins (only in POSIX mode)

                      • functions

                      • builtins

                      • hashed commands (read help hash)

                      • external commands (searched in $PATH dirs)

                      The first match found will be executed.



                    The order may be changed by (for test):



                    • To bypass just alias, use test or any other kind of expansion.

                    • To ignore functions and aliases, use command test.

                    • To execute a builtin, use builtin test.

                    • To execute an external application, use an explicit path: /bin/test.

                    So, a function defined as:



                    $ ls() ls; 


                    Will be called in an infinite loop. As also an script which calls the same first word. Something like:



                    #!/bin/bash
                    $0 "$@"


                    Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).



                    The order will be shown by running type -a:



                    $ test() echo test function; 
                    $ alias test=test
                    $ type -a
                    test is aliased to `test'
                    test is a function
                    test ()

                    echo test function

                    test is a shell builtin
                    test is /usr/bin/test


                    The function (as defined in the question) will only bypass the alias with vim but not the function. To bypass alias and functions, use command. This function should do what you need:



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
                    fi






                    share|improve this answer
















                    Why do the other options not work as I would expect them to?




                    Because there are some details in the way a command line gets executed.



                    The basic concept is that the first word of a command line is the command that the shell will search and try to execute, in this order:




                    1. The command line is split on metacharacters (mostly):




                      metacharacters

                      A character that, when unquoted, separates words. One of the following:

                      | & ; ( ) < > space tab newline





                    2. If the first unquoted word match an alias word, it is replaced by the alias definition. To avoid loops, this is executed only once if the new first word match an alias being expanded. This makes this work without loops:



                      $ alias ls='ls -la'
                      $ ls dir # will expand to `ls -la dir`


                      Note that: If the last character of the alias value is a blank, then the next command word following the alias is also checked for alias expansion.




                    3. If the (resulting) first word is an expansion (like a $var), it is replaced (and subject to IFS word splitting (and globing) if not quoted):



                      $ var='echo -e '
                      $ $var test 'twords' # will expand to `echo -e test words`
                      test words



                    4. The resulting first word after above expansions (after optional variable assignments) will be searched in this order:



                      • special builtins (only in POSIX mode)

                      • functions

                      • builtins

                      • hashed commands (read help hash)

                      • external commands (searched in $PATH dirs)

                      The first match found will be executed.



                    The order may be changed by (for test):



                    • To bypass just alias, use test or any other kind of expansion.

                    • To ignore functions and aliases, use command test.

                    • To execute a builtin, use builtin test.

                    • To execute an external application, use an explicit path: /bin/test.

                    So, a function defined as:



                    $ ls() ls; 


                    Will be called in an infinite loop. As also an script which calls the same first word. Something like:



                    #!/bin/bash
                    $0 "$@"


                    Will re-execute the same script in a loop until the kernel breaks out (if the kernel in use has a limit of successive calls to scripts).



                    The order will be shown by running type -a:



                    $ test() echo test function; 
                    $ alias test=test
                    $ type -a
                    test is aliased to `test'
                    test is a function
                    test ()

                    echo test function

                    test is a shell builtin
                    test is /usr/bin/test


                    The function (as defined in the question) will only bypass the alias with vim but not the function. To bypass alias and functions, use command. This function should do what you need:



                    vim() 
                    if [ -w "$1" ]; then
                    command vim "$1"
                    else
                    sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1"
                    fi







                    share|improve this answer















                    share|improve this answer



                    share|improve this answer








                    edited Jul 11 at 13:42


























                    answered Jul 11 at 2:42









                    Isaac

                    6,2331632




                    6,2331632











                    • This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
                      – Charles Duffy
                      Jul 11 at 12:07











                    • Consider instead sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1" -- then the ~ is evaluated by the parent shell before sudo even starts, and you're passing the filename out-of-band from the script text.
                      – Charles Duffy
                      Jul 11 at 12:09











                    • (er, should have been "with touch ..." rather than "named touch ..." above).
                      – Charles Duffy
                      Jul 11 at 12:14











                    • @CharlesDuffy Sure, that's correct, thanks.
                      – Isaac
                      Jul 11 at 13:44
















                    • This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
                      – Charles Duffy
                      Jul 11 at 12:07











                    • Consider instead sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1" -- then the ~ is evaluated by the parent shell before sudo even starts, and you're passing the filename out-of-band from the script text.
                      – Charles Duffy
                      Jul 11 at 12:09











                    • (er, should have been "with touch ..." rather than "named touch ..." above).
                      – Charles Duffy
                      Jul 11 at 12:14











                    • @CharlesDuffy Sure, that's correct, thanks.
                      – Isaac
                      Jul 11 at 13:44















                    This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
                    – Charles Duffy
                    Jul 11 at 12:07





                    This has serious security faults. Let's say you're trying to open a file created (by a different/malicious user) named touch $'/tmp/$(rm -rf ~)'$(rm -rf ~)''; the double quotes don't save you from command substitution (neither would literal single quotes, due to the ones in the filename). And moreover, you're running that injected command as root.
                    – Charles Duffy
                    Jul 11 at 12:07













                    Consider instead sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1" -- then the ~ is evaluated by the parent shell before sudo even starts, and you're passing the filename out-of-band from the script text.
                    – Charles Duffy
                    Jul 11 at 12:09





                    Consider instead sudo HOME="$HOME" bash -c 'command vim "$@"' _ -u ~/.vimrc "$1" -- then the ~ is evaluated by the parent shell before sudo even starts, and you're passing the filename out-of-band from the script text.
                    – Charles Duffy
                    Jul 11 at 12:09













                    (er, should have been "with touch ..." rather than "named touch ..." above).
                    – Charles Duffy
                    Jul 11 at 12:14





                    (er, should have been "with touch ..." rather than "named touch ..." above).
                    – Charles Duffy
                    Jul 11 at 12:14













                    @CharlesDuffy Sure, that's correct, thanks.
                    – Isaac
                    Jul 11 at 13:44




                    @CharlesDuffy Sure, that's correct, thanks.
                    – Isaac
                    Jul 11 at 13:44


                    Popular posts from this blog

                    How to check contact read email or not when send email to Individual?

                    Bahrain

                    Postfix configuration issue with fips on centos 7; mailgun relay