Search code examples
bashcurlslack-apixclip

Programmatically Executing Clipboard Contents within Bash Shell (or Better Approach)


I'm currently working on a bash script which will send the current timestamp and the IP of a system (running Ubuntu 18.04 LTS) to a Slack channel using a webhook as shown below:

##!/bin/bash

command=(curl -X POST -H \'Content-type: application/json\' --data \'{\"text\":\")
command+=("$(date)")
command+=(" IP: ")
command+=("$(hostname -I)")
command+=(\"}\' https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX)
"${command[@]}"

which is meant to replicate the following working command while allowing for a variable timestamp and IP: curl -X POST -H 'Content-type: application/json' --data '{"text":"Tue Jul 14 15:26:50 EDT 2020 IP: XX.X.X.XX"}' https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

This returns a litany of errors (the IP host name was manually changed to X's for this post):

curl: (6) Could not resolve host: application
curl: (3) Port number ended with ':'
curl: (3) Port number ended with ' '
curl: (3) Host name 'X.X.X.XX ' contains bad letter
curl: (3) [globbing] unmatched close brace/bracket in column 2

I was able to determine that replacing "${command[@]}" with echo "${command[@]}" | xclip -selection clipboard" and then manually pasting it into the terminal (right click -> paste) works perfectly. Unfortunately my first thought of using xclip -selection clipboard -o seems to simply return the string in the same way echo "${command[@]}" does.

Is there a way to programmatically paste the clipboard contents into the shell as a command and execute them, or even a way to adjust the initial "${command[@]}" call to execute the string? If a clearly better approach exists that I've failed to see please feel free to tell me.

I apologize in advance if this is trivial to those who see it, but I am incredibly new to both Linux systems and bash scripting. Any help would be greatly appreciated.


Solution

  • The problem is that you've created an array with the following elements (one per line):

    curl
    -X
    POST
    -H
    'Content-type:
    application/json'
    --data
    '{"text":"
    <output of date>
     IP:
    <output of hostname -I>
    "}'
    https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
    

    So, executing this array as a command has 'Content-type:, application/json', '{"text":", <output of date>, IP:, etc. as separate command arguments. In other words, the quotes in the elements of the command array aren't meaningful to bash when "${command[@]}" is expanded. When you copy and paste the result of the expansion into your terminal, bash is no longer treating it as the result of an array expansion. Instead, it treats that input as a string to be parsed as a top-level command, where the quotes are meaningful.

    To fix it, you just need to make sure that each element of the command array corresponds to an argument of the command that you intend to execute:

    command=(curl -X POST -H 'Content-type: application/json')
    command+=(--data '{"text":"'"$(date)"' IP: '"$(hostname -I)"'"}')
    command+=(https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX)
    "${command[@]}"
    

    Ultimately though, unless you need the command array for something else in the script, you can avoid the array and its substitution altogether:

    curl \
        -X POST \
        -H 'Content-type: application/json' \
        --data '{"text":"'"$(date)"' IP: '"$(hostname -I)"'"}' \
        https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
    

    The escaping for --data is admittedly confusing; to avoid escaping double quotes I've mixed single and double quoted strings (in bash putting them right next to each other concatenates them, i.e., "foo"'bar' becomes foobar).