osx - Write pipe result to variable

07
2014-07
  • Danijel J

    I need to be able to write whether the test for a grep is either TRUE or FALSE to a variable so I can use it later

    For the following, if I run

    defaults read com.apple.Finder | grep "AppleShowAllFiles"
    

    on my system, it would return

    AppleShowAllFiles = FALSE;
    

    Cool. So now I want to pipe this response to a test of some kind. This is where I get stuck.

    I think if I can pipe/assign this output to a specified variable, I would be able to run a test on it. Now, just say, I've assigned the value of this output to a variable, in this case I will use $ASAF as my variable, I can run it in a test like this

    if [ $ASAF = "AppleShowAllFiles = TRUE;" ]; then
    defaults write com.apple.Finder AppleShowAllFiles FALSE
    killall Finder
    else
    defaults write com.apple.Finder AppleShowAllFiles True
    killall Finder
    fi

    If there is some other way to do this, I would be more than open to options. I've not had to do something like this for a while, and I'm a bit stumped. I searcehd Google a bit, but it was all answers without explanations and using the return value of 0 or 1. I think the returned output being assigned to a variable would be more appropriate, as then I can use it over and over in the script as need be.

  • Answers
  • rici

    If you just want to save the return code (success/failure) of a command, use $?:

    # grep -q to avoid sending output to `stdout` and to stop if it finds the target.
    defaults read com.apple.Finder | grep -q "AppleShowAllFiles"
    NOTFOUND=$?
    
    # Note that we use an *arithmetic* test, not a string test, and that we have to
    # invert the arithmetic value.
    
    if ((!NOTFOUND)); then
      # not NOTFOUND is FOUND
      # ...
    fi
    

    Alternatively, you could save the output in a variable, if, as in your example, you need the actual string. If so, your solution is reasonable, but be aware that parsing the output of programs like defaults can be quite brittle, since minor format changes will make patterns suddenly fail to match. Try to make your patterns as general as possible:

    # Here we use the `-m1` flag to stop matching as soon as the first line matches
    ASAF_STRING=$(defaults read com.apple.Finder | grep -m1 "AppleShowAllFiles")
    
    # Try to parse true or false out of the string. We do this in a subshell to
    # avoid changing the shell option in the main shell.
    ASAF=$(
      shopt -s nocasematch
      case $ASAF_STRING in 
        *true*) echo TRUE;;
        *false*) echo FALSE;;
        *) echo Could not parse AppleShowAllFiles >> /dev/stderr;;
      esac
    )
    
  • Eric

    I see two ways of doing this, depending on whether you actually need the variable more than once or not.

    If you only need the variable once, you can just combine the grep into your if statement, i.e.

    if defaults read com.apple.Finder | grep -q "AppleShowAllFiles" ; then ...

    Or, if you need to use the variable more than once, you could do something like this

    defaults read com.apple.Finder | grep -q "AppleShowAllFiles"

    ASAF=$? ##$? is equal to the return value of the last command

    if [ "$ASAF" -eq "0"]; then ...

  • Tech29

    You don't need to use grep at all:

    [[ $(defaults read com.apple.finder AppleShowAllFiles)  0 ]] && bool=true || bool=false
    defaults write com.apple.finder AppleShowAllFiles -bool $bool
    osascript -e 'quit app "Finder";
    

    defaults read prints boolean values as 1 or 0.


  • Related Question

    osx - Can Automator branch based on the value of a variable or the result of a step?
  • atroon

    I am editing an Automator workflow that currently takes several stored spreadsheets and emails them to different individuals. The spreadsheets are stored on a Windows server and are created by a separate process not germane to this discussion. But once they're created, I want the user to be able to click on this action and have the mail messages (usually 8 of them) pop up to be sent.

    All this works fine, as long as the Mac's connection to the server exists. If the server connection isn't present, things fail silently, because the files aren't there.

    I've looked through all the possible steps you can add in Utilities, Other, and Finder (and all the rest) and I can't find any way for it to do something like e.g. 'Skip this step if the variable ServerConnected is not null'.

    I can automagically connect to the server with an Automator action, but if it's already connected, I get another connection, which causes me trouble later on...unmounting unmounts the first one and the server stops showing up, but then the path in /Volumes is still there...it's a road I don't want to go down.

    So, is there any way to use variables to check if a condition is true or false and skip steps based on that? Or any other way to accomplish the task? I don't use Automator too much, I'm more comfortable with scripting the old fashioned way so even cracking open the .workflow file and editing parameters by hand is an option, presuming you can do that. I'm editing this particular workflow because it was developed by someone else, and it really does do a good job in general, except for the server connection glitch.


  • Related Answers
  • ridogi

    You could split it into 3 automator apps and branch with a shell script. Run the normal workflow up to the connection check, and then use the automator action Do Shell Script.

    That script could be something like:

    #! /bin/bash
    
    if
            <your command to test connection>
    then
            open /pathto/automator_connected.app
    else
            open /pathto/automator_not_connected.app
    fi
    

    automator_connected.app would contain the remainder of you automator workflow

    automator_not_connected.app would contain the actions to take if the server connection fails