0

I have a script that copies files between servers. I am using the lsof command to make sure that the file is not being written to before being moved. The user running the script and the user writing to the file are different, so I need to sudo to the file owner. Here is the relevant line in the sudoers file :

userA ALL=(userB:userB) NOPASSWD: ALL

In the main script (ran as userA), I have tried calling sudo then the subscript containing the lsof command:

sudo su - userB -c 'source ./getOpenFiles.sh'

getOpenFiles.sh has this one line:

#!/bin/bash 
lsofResult=$(/usr/sbin/lsof "${sourcePath}")

I have also tried calling the subscript:

source ./getOpenFiles.sh

Then have the first line of the subscript be the sudo:

#!/bin/bash
sudo su - banjobs
lsofResult=$(/usr/sbin/lsof "${sourcePath}")`.

Neither solution is working.

5
  • When you run sudo su - banjobs, the next command doesn't run until after your sudo su finishes and exits, and nothing left is running as banjobs. Commented Dec 20, 2016 at 21:12
  • Which is to say that it doesn't evaluate lsofResult as banjobs. Commented Dec 20, 2016 at 21:12
  • ...and if you use su -c 'source somescript', then somescript is run by sh, not bash, no matter what its shebang says. And if your sh doesn't support source, then it doesn't run at all. (The POSIX-compatible way to write source is . somescript, not source somescript). Commented Dec 20, 2016 at 21:13
  • ...which is to say: There's a lot wrong here, and not all of it has to do with source. (Though if you expect source to result in the variable being set in the parent shell -- that is, the shell that's running your sudo command, that won't ever work, since sudo is itself a subprocess -- the point of source is to do something directly in your current shell process; if you do anything in a subprocess, that entirely defeats the point). Commented Dec 20, 2016 at 21:15
  • BTW, as an aside, code intended to be sourced into bash should be named with a .bash extension, rather than a .sh extension -- that way it's more obvious to readers that the code isn't intended to be compatible with POSIX sh, but specifically is only safe to source into interpreters with bash extensions. (Not that it matters for the specific code you gave here, which other than source is POSIX-sh-compatible, but consider it a best-practices note). By contrast, for commands intended to be executed rather than sourced, it's more appropriate not to have any file extension at all. Commented Dec 20, 2016 at 21:39

1 Answer 1

1

What you actually want is something more like:

lsofResult=$(sudo -u banjobs lsof "${sourcePath}")

Let's go over why the other approaches didn't work one-at-a-time:

  • Running source under sudo su -c

    sudo su - userB -c 'source ./getOpenFiles.sh'
    

    ...uses sudo to run su, which runs sh -c 'source ./getOpenFiles.sh'. This doesn't work for several independent reasons:

    1. sh -c 'source ./getOpenFiles.sh' relies on the source keyword being available in /bin/sh, but this is a bash extension.

    2. Even if your /bin/sh is provided by bash, this still defeats the purpose of using source: By starting a new copy of /bin/sh and sourcing your script into that, you're defining the variable in the new shell, not in the original shell that started sudo su.

  • Running sudo su - banjobs, followed by lsofResult=$(/usr/sbin/lsof "${sourcePath}")

    ...means that lsofResult=$(...) doesn't run until after sudo su - banjobs exits. If sudo su - banjobs has exited, then the current user isn't banjobs any more, so the sudo command has no effect whatsoever on the lsof.


Demonstrating, in detail, how to test this (for folks who don't have a banoff or userB account on their system):

# place relevant contents in sourceme.bash
# switching from lsof to fuser for this test since OS X lsof does not accept a directory
# as an argument.
cat >sourceme.bash <<'EOF'
lsofResult=$(sudo -u root fuser "${sourcePath}" 2>&1)
EOF

# set sourcePath in the outer shell
sourcePath=$PWD
source sourceme.bash
declare -p lsofResult

...yields, on my system, output akin to the following:

declare -- lsofResult="/Users/chaduffy/tmp: 17165c 17686c 17687c 17688c 17689c 17690c"

...showing that the code in question did in fact work as described.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your answer Charles. Unfortunately, your solution still did not work. I tried it inside both the subscript and the main script.
@Stu, I'd need to see a full reproducer, showing exactly how you're using it (both the invocation and the evaluation of the collected value). This certainly does "work" when used correctly -- I'm happy to edit an example into the question showing that.
@Stu, ...such an example has been added.
Thank you for the suggestion, Charles. We have decided to allow the script to run under the owner of the files, so the whole use of sudo is now unnecessary. Thank you again for your help.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.