How to create a app with automator to run a bash script?

Discussion in 'Mac Programming' started by Technologx, Feb 13, 2016.

  1. Technologx macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #1
    I want to create a app kind of like Ds Store Remover but it will remove directories instead of just one file. So I was wondering how do I go about doing this I have a bash script the code is below I just need to know what to do. To have the app ask for root password and allow the user to select the directory that they want these directories.



    sudo rm -rf “.Thrashes”





    sudo rm -rf “.Spotlight-V100”





    sudo rm -rf “.Trash”





    sudo rm -rf “.DS_Store”
     
  2. 0d085d macrumors regular

    Joined:
    Apr 23, 2015
    #2
    It sounds like you simply want to run your bash script as an application. I'd do this by rewriting it in Applescript:

    Code:
    set TargetDir to (choose folder with prompt "Choose the folder you want to clean")
    set TargetPath to POSIX path of TargetDir
    set ShellScript to "cd " & TargetPath & "
    rm -rf .Trashes
    rm -rf .Spotlight-V100
    rm -rf .Trash
    rm -rf .DS_Store"
    do shell script ShellScript with administrator privileges
    You can then do File -> Save... and choose Application as the file format.

    Note that doing it this way lets the operating system deal with granting root permissions - it doesn't mess around with handling passwords itself.

    BE VERY CAREFUL. If the cd command fails for any reason then the rm commands will carry on regardless, most likely in /. It would probably be a good idea to get the bash script to exit if the cd fails.
     
  3. superscape macrumors 6502a

    superscape

    Joined:
    Feb 12, 2008
    Location:
    East Riding of Yorkshire, UK
    #3
    ...or alternatively, if you *really* want to do it in Automator (and there's no reason why you shouldn't) then just select "Application" when you create a new Automator action and use the Run Shell Script action.
     
  4. Kornelis macrumors member

    Kornelis

    Joined:
    Dec 4, 2010
    Location:
    Amsterdam, NL
    #4
    If you separate the bash commands with && symbols, the sequence will stop if any command fails:

    command1 && command2 && command3
     
  5. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #5
    How would I make this into a actual app using Xcode?
     
  6. superscape macrumors 6502a

    superscape

    Joined:
    Feb 12, 2008
    Location:
    East Riding of Yorkshire, UK
    #6
    Not sure why you'd need to make it into an Xcode app, to be honest. However, you have a few options, including:

    • Rewrite it in Objective-C / Swift and use NSTask to execute your shell scripts (http://www.raywenderlich.com/36537/nstask-tutorial)
    • Create an AppleScriptObjC app and use the code you already have
    • Rewrite it in pure Objective-C from scratch

    ...and I'm sure someone will recommend using Python at some point. I'm not going to though. ;-)
     
  7. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #7
    What line of code do I need to prompt user for the root password because of su?
     
  8. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #8
    It's done by this line of AppleScript:
    Code:
    do shell script ShellScript with administrator privileges
    
    The with administrator privileges will prompt for a password using a dialog, and run the shell script as root if it gets the right info.

    As a test, I recommend that you run a completely harmless shell script with administrator privileges, so you can see how it works. An example would be the 'id' shell command, with output redirected to a safe file.
     
  9. 0d085d macrumors regular

    Joined:
    Apr 23, 2015
    #9
    Possibly not relevant, but I feel worth having on record:

    If you're ever going to try to distribute your app then it's always always always worth managing authentication by letting the operating system deal with it (such as by declaring with administrator privileges in Applescript). If I were to get a dialog box asking for any system password (let alone for an account with sudo privileges) from anything other than the OS itself, I wouldn't be using that app again.

    If you're just using the app yourself then you can do what you like if it's easier. Who cares if it looks suspicious? In this case I'd argue that it's actually easier to run the script as an administrator legitimately than to mess around trying to pass passwords to sudo, but that's your choice.
     
  10. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #10
    I figured out how to create the app with Xcode but I need to have the app promt for a root password after the directory is selected.
     
  11. 0d085d macrumors regular

    Joined:
    Apr 23, 2015
    #11
    I assume you're using Swift. From here, you could do something like:

    Code:
    func doScriptWithAdmin(inScript:String) -> String{
        let script = "do shell script \"\(inScript)\" with administrator privileges"
        var appleScript = NSAppleScript(source: script)
        var eventResult = appleScript.executeAndReturnError(nil)
        if !eventResult {
            return "ERROR"
        }else{
            return eventResult.stringValue
        }
    }
    Then just pass your script (without sudo calls) to that function.
     
  12. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #12
    Actually this is what I got:
    Code:
    - (IBAction)browse:(id)sender{
    
    
       
    
       
    
        NSOpenPanel* openDlg = [NSOpenPanelopenPanel];
    
       
    
        // Enable the selection of files in the dialog.
    
        [openDlg setCanChooseFiles:NO];
    
       
    
        // Enable the selection of directories in the dialog.
    
        [openDlg setCanChooseDirectories:YES];
    
       
    
        // Change "Open" dialog button to "Select"
    
        [openDlg setPrompt:@"Select"];
    
       
    
        // Display the dialog.  If the OK button was pressed,
    
        // process the files.
    
        if ( [openDlg runModal] == NSModalResponseOK )
    
        {
    
            // Get an array containing the full filenames of all
    
            // files and directories selected.
    
            NSArray* files = [openDlg URLs];
    
           
    
            // Loop through all the files and process them.
    
            for( int i = 0; i < [files count]; i++ )
    
            {
    
               
    
                NSString* fileName = [files objectAtIndex:i];
    
                NSLog(@"file: %@", fileName);
    
               
    
                NSTask *task = [[NSTaskalloc] init];
    
                [task setLaunchPath:@"/bin/bash"];
    
                [task setArguments:@[@"-c", @"/usr/local/bin/hiddenfileremover"]];
    
                [task launch];
    
               
    
                [openDlg setPrompt:@"Select"];
    
    
            }
    
        }
    
    }
    
     
  13. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #13
    This is the script that the app will be running:

    Code:
    #!/bin/bash
    
    sudo rm -rf .Trashes
    
    sudo rm -rf .Spotlight-V100
    
    sudo rm -rf .DS_Store
    
    sudo rm -rf .fseventsd
    
     
  14. 0d085d macrumors regular

    Joined:
    Apr 23, 2015
    #14
    Right, I haven't tested any of this myself, but hopefully these couple of thoughts will help:

    1) When getting the filepath of the directory, you should be doing:

    Code:
    NSString* fileName = [[files objectAtIndex:i] path];
    This will get you a proper POSIX filepath instead of a quoted URL path (pretty sure that files is an array of NSURL objects - if not this will need to be something else).

    2) Because you have the shebang (#!/bin/bash) in your script, you don't need to invoke bash to run it. The system can do that itself. This code:

    Code:
    NSTask *task = [[NSTaskalloc] init];
    [task setLaunchPath:@"/bin/bash"];
    [task setArguments:@[@"-c", @"/usr/local/bin/hiddenfileremover"]];
    [task launch];
    should be rewritten as:

    Code:
    NSTask *task = [[NSTaskalloc] init];
    [task setLaunchPath:@"/usr/local/bin/hiddenfileremover"];
    [task setArguments:@[fileName]];
    [task launch];
    and your script rewritten as:

    Code:
    #!/bin/bash
    rm -rf $1/.Trashes
    rm -rf $1/.Spotlight-V100
    rm -rf $1/.DS_Store
    rm -rf $1/.fseventsd
    
    (Slashes might need to be removed, or you might need to strip trailing slashes from fileName. You can experiment with that yourself.)


    3) If you add this class to your project: https://github.com/sveinbjornt/STPrivilegedTask then you can simply replace your NSTask with an STPrivelegedTask:

    Code:
    STPrivilegedTask *privilegedTask = [[STPrivilegedTask alloc] init];
    [privilegedTask setLaunchPath:@"/usr/local/bin/hiddenfileremover"];
    NSArray *args = [NSArray arrayWithObject:fileName];
    [privilegedTask setArguments:args];
    
    OSStatus err = [privilegedTask launch];
    if (err != errAuthorizationSuccess) {
        if (err == errAuthorizationCanceled) {
            NSLog(@"User cancelled");
        } else {
            NSLog(@"Something went wrong");
        }
    } else {
        // task successfully launched
    }
    
    and that will run the task as root.
     
  15. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #15
    How would I add this to my code I'm getting all kinds of errors, sorry I'm new to Mac coding?
     
  16. 0d085d macrumors regular

    Joined:
    Apr 23, 2015
    #16
    Code:
    NSString* fileName = [[files objectAtIndex:i] path];
    should replace
    Code:
    NSString* fileName = [files objectAtIndex:i];


    and:

    Code:
    STPrivilegedTask *privilegedTask = [[STPrivilegedTask alloc] init];
    [privilegedTask setLaunchPath:@"/usr/local/bin/hiddenfileremover"];
    NSArray *args = [NSArray arrayWithObject:fileName];
    [privilegedTask setArguments:args];
    
    OSStatus err = [privilegedTask launch];
    if (err != errAuthorizationSuccess) {
        if (err == errAuthorizationCanceled) {
            NSLog(@"User cancelled");
        } else {
            NSLog(@"Something went wrong");
        }
    } else {
        // task successfully launched
    }
    
    should replace:

    Code:
    NSTask *task = [[NSTaskalloc] init];
    [task setLaunchPath:@"/bin/bash"];
    [task setArguments:@[@"-c", @"/usr/local/bin/hiddenfileremover"]];
    [task launch];
    And you'll need to download STPrivelegedTask.h and STPrivelegedTask.m from here and add
    Code:
    #import "STPrivilegedTask.h"
    to the top of your code.

    I wasn't expecting this thread to turn to Objective C, which I'll admit is out of my comfort zone. If you get any errors it would be helpful if you could post them.
     
  17. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #17
    Alright I did what you said now I'm getting this as a error:
    for this line:
    Code:
    NSOpenPanel* openDlg = [NSOpenPanelopenPanel];
    .
     
  18. 0d085d macrumors regular

    Joined:
    Apr 23, 2015
    #18
    There should be a space between NSOpenPanel and openPanel.
    Code:
    NSOpenPanel* openDlg = [NSOpenPanel openPanel];
     
  19. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #19
    Alright that code is working it's just I need to get my bash script working correctly it's not deleting the files like it should. Do I possibly need to link any frameworks?
     
  20. 0d085d macrumors regular

    Joined:
    Apr 23, 2015
    #20
    You shouldn't need to link in anything else. It could be any number of things wrong - your script not being called properly or a bad filepath being passed to it, for example.

    Did you remember to remove the sudo commands? Leaving them in may cause the script to fail.

    I can't really debug this for you. You already have solutions in Applescript (post #2) and Automator (post #3) for the problem you originally asked about. If you're trying to use Xcode for this as a learning exercise, I'd recommend finding some tutorial exercises online instead of inventing your own.
     
  21. Technologx thread starter macrumors member

    Technologx

    Joined:
    Dec 17, 2014
    Location:
    USA
    #21
    Yea I remembered to remove the sudo commands from the script and linked the script correctly. If I was to include the script with the app if I do that how would I go about linking it then?
     

Share This Page