diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java index be6932983ac..cc4d76ed739 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Shell.java @@ -83,8 +83,23 @@ abstract public class Shell { } } - /** a Unix command to get the current user's name */ - public final static String USER_NAME_COMMAND = "whoami"; + /** + * Quote the given arg so that bash will interpret it as a single value. + * Note that this quotes it for one level of bash, if you are passing it + * into a badly written shell script, you need to fix your shell script. + * @param arg the argument to quote + * @return the quoted string + */ + static String bashQuote(String arg) { + StringBuilder buffer = new StringBuilder(arg.length() + 2); + buffer.append('\''); + buffer.append(arg.replace("'", "'\\''")); + buffer.append('\''); + return buffer.toString(); + } + + /** a Unix command to get the current user's name: {@value}. */ + public static final String USER_NAME_COMMAND = "whoami"; /** Windows CreateProcess synchronization object */ public static final Object WindowsProcessLaunchLock = new Object(); @@ -134,7 +149,7 @@ abstract public class Shell { /** a Unix command to get the current user's groups list */ public static String[] getGroupsCommand() { return (WINDOWS)? new String[]{"cmd", "/c", "groups"} - : new String[]{"bash", "-c", "groups"}; + : new String[]{"groups"}; } /** @@ -144,17 +159,21 @@ abstract public class Shell { * i.e. the user's primary group will be included twice. */ public static String[] getGroupsForUserCommand(final String user) { - //'groups username' command return is non-consistent across different unixes - return (WINDOWS)? new String[] { WINUTILS, "groups", "-F", "\"" + user + "\""} - : new String [] {"bash", "-c", "id -gn " + user - + "&& id -Gn " + user}; + //'groups username' command return is inconsistent across different unixes + if (WINDOWS) { + return new String[] + {getWinUtilsPath(), "groups", "-F", "\"" + user + "\""}; + } else { + String quotedUser = bashQuote(user); + return new String[] {"bash", "-c", "id -gn " + quotedUser + + "; id -Gn " + quotedUser}; + } } /** a Unix command to get a given netgroup's user list */ public static String[] getUsersForNetgroupCommand(final String netgroup) { //'groups username' command return is non-consistent across different unixes - return (WINDOWS)? new String [] {"cmd", "/c", "getent netgroup " + netgroup} - : new String [] {"bash", "-c", "getent netgroup " + netgroup}; + return new String[] {"getent", "netgroup", netgroup}; } /** Return a command to get permission information. */ @@ -260,8 +279,9 @@ abstract public class Shell { */ public static String[] getRunScriptCommand(File script) { String absolutePath = script.getAbsolutePath(); - return WINDOWS ? new String[] { "cmd", "/c", absolutePath } : - new String[] { "/bin/bash", absolutePath }; + return WINDOWS ? + new String[] {"cmd", "/c", absolutePath } + : new String[] {"/bin/bash", bashQuote(absolutePath) }; } /** a Unix command to set permission */ @@ -744,7 +764,13 @@ abstract public class Shell { /** Execute the shell command. */ public void execute() throws IOException { - this.run(); + for (String s : command) { + if (s == null) { + throw new IOException("(null) entry in command string: " + + StringUtils.join(" ", command)); + } + } + this.run(); } @Override diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java index 6fb1ce81cdc..92d057fcff5 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/TestShell.java @@ -209,4 +209,11 @@ public class TestShell extends TestCase { assertEquals(2, command.getRunCount()); } } + + @Test + public void testBashQuote() { + assertEquals("'foobar'", Shell.bashQuote("foobar")); + assertEquals("'foo'\\''bar'", Shell.bashQuote("foo'bar")); + assertEquals("''\\''foo'\\''bar'\\'''", Shell.bashQuote("'foo'bar'")); + } }