Sample Header Ad - 728x90

Would #! /bin/sh - shebang prevent from execution on other operating systems? How to create shell agnostic portable script?

1 vote
2 answers
153 views
If I include a shebang line in my script like following #! /bin/sh Would it prevent from execution on other operating systems like Windows which may not have sh in bin folder? Is this better to omit shebang line to make a portable script? How to write a operating system agnostic, shell agnostic, portable script which would execute in the same way in all operating systems or shells? If the script has to be executed the same way on Mac OS, Windows, Ubuntu, any other Linux or any other operating systems? --- Assumptions. I assume to prepare a script for a workshop where workshop participants may use different operating systems and may have different shells; I share preparation notes to install all needed tools like jq, sed, sfdx, curl and I assume the script would run the same way on all machines and all the operating systems. I assume workshop participants mainly use Mac OS, Windows, Ubuntu. Also I assume that participants may install Git Bash tool on Windows if there is no posix compatible shell. --- The script itself unwrap2.sh verbose=$1 # deploy data cloud Data Kit sf project deploy start -d dc # unwrap Data Kit components x=$(sf apex run -f scripts/unwrap.apex --json | jq '.result.logs' -r) x1=${x#*EXECUTION_STARTED} x2=${x1#*Result is \(success): } if [[ "$verbose" = "verbose" ]]; then echo "x2: $x2" fi # Actually this was my mistake here which I have posted in another question # this line should be x3=$(echo "$x2" | head -n 1) to make it work # However, original version of this script with this error didn't have the doublequotes here x3=$(echo $x2 | head -n 1) if [[ "$verbose" = "verbose" ]]; then echo "x3: $x3" fi status=$(sf data query -q "select Id,Status FROM BackgroundOperation WHERE Id = '$x3'" --json | jq '.result.records.Status' -r) if [[ "$verbose" = "verbose" ]]; then echo "select Id,Status FROM BackgroundOperation WHERE Id = '$x3'" fi echo "Status: $status" while [[ "$status" != "Complete" ]]; do sleep 10 status=$(sf data query -q "select Id,Status FROM BackgroundOperation WHERE Id = '$x3'" --json | jq '.result.records.Status' -r) echo "Status: $status" done Also, I have other scripts, like setup.sh email=$1 integrationUsername=$2 defaultFolder="force-app/main/default" mkdir "$defaultFolder/connectedApps" cp "template/connectedApps/Integration.connectedApp-meta.xml" "$defaultFolder/connectedApps" cp "template/data/int.csv" "data" SEDOPTION= if [[ "$OSTYPE" == "darwin"* ]]; then SEDOPTION="-i ''" fi # transform connected app from the template folder into default folder key=${integrationUsername//@/..} echo "Key: $key" sed -e "s/{{email}}/$email/g" -e "s/{{integration.username}}/$integrationUsername/g" -e "s/{{key}}/$key/g" "template/connectedApps/Integration.connectedApp-meta.xml" > "$defaultFolder/connectedApps/Integration.connectedApp-meta.xml" # transform integration user sed -e "s/{{email}}/$email/g" -e "s/{{integration.username}}/$integrationUsername/g" "template/data/int.csv" > "data/int.csv" # create integration user ./insertUsers.sh data/int.csv # deploy connected app sf project deploy start -d force-app # assign perm set sf org assign permset -n GenieAdmin sf org assign permset -n GenieAdmin -b $integrationUsername sf org open -p "lightning/setup/SetupOneHome/home?setupApp=audience360" which invokes insertUsers.sh data=$(cat $1) username=$(sfdx force:org:display --json | jq '.result.username' -r) echo "Username = $username" echo "Last part is ${username##*.}" sandbox=${username##*.} echo "sandbox ${sandbox}" if [[ $sandbox == 'com' ]] then sandbox=${username%%@*} echo "First part is ${sandbox}" fi data=${data//.org/.$sandbox} echo "$data" > us.csv sf data upsert bulk -s User -f us.csv -w 500 -i Id --json > upsert.json echo $(cat upsert.json) users=$(cat upsert.json | jq '.result.records.successfulResults[].sf__Id' -r) echo "Users before transformation $users" users="${users//$'\n'/','}" echo "Users after transformation $users" code=$(cat scripts/updateUser.apex) echo "${code//users/$users}" > temp.apex sf apex run --file temp.apex rm temp.apex rm us.csv rm upsert.json IFS="$old_ifs" Also, I have deploy script ./deploy.sh verbose verbose=$1 # deploy data cloud Data Kit sf project deploy start -d dc # unwrap Data Kit components scDomain=$(sf org display --json | jq '.result.instanceUrl' -r) if [[ "$verbose" = "verbose" ]]; then echo "Sales Cloud domain: $scDomain" fi scToken=$(sf org display --json | jq '.result.accessToken' -r) if [[ "$verbose" = "verbose" ]]; then echo $scToken fi version=$(cat sfdx-project.json | jq '.sourceApiVersion' -r) result=$(curl --location --request POST "$scDomain/services/data/v$version/actions/custom/flow/sfdatakit__DeployDataKitComponents" \ --header "Authorization: Bearer $scToken" \ --header 'Content-Type: application/json' \ --data @data/unwrap.json ) figuid=$(echo $result | jq '..outputValues.Flow__InterviewGuid' -r) if [[ "$verbose" = "verbose" ]]; then echo "result: $result" echo "figuid: $figuid" fi sf data query -q "SELECT Id, BundleName, ComponentName, ComponentTemplateId, ComponentType, DataKitName, DataSpaceName, DeployJob, DeploymentError, DeploymentStatus , FlowInterviewIdentifier, Name, PublisherOrgComponentId, SubscriberOrgComponentId, TemplateVersion FROM DataKitDeploymentLog WHERE FlowInterviewIdentifier = '$figuid'" --json > soql.json count=$(cat soql.json | jq '.result.records | length') if [[ "$verbose" = "verbose" ]]; then echo "Count: $count" fi if [ "$count" = "1" ]; then x="count is one" else x="count is not one" fi if [[ "$verbose" = "verbose" ]]; then echo "x: $x" fi while [ "$count" = "1" ]; do echo "Unwrapping Data Kit components in progress, waiting another 5 seconds..." sleep 5 sf data query -q "SELECT Id, BundleName, ComponentName, ComponentTemplateId, ComponentType, DataKitName, DataSpaceName, DeployJob, DeploymentError, DeploymentStatus , FlowInterviewIdentifier, Name, PublisherOrgComponentId, SubscriberOrgComponentId, TemplateVersion FROM DataKitDeploymentLog WHERE FlowInterviewIdentifier = '$figuid'" --json > soql.json count=$(cat soql.json | jq '.result.records | length') done deploymentStatus1=$(cat soql.json | jq '.result.records.DeploymentStatus' -r) deploymentStatus2=$(cat soql.json | jq '.result.records.DeploymentStatus' -r) if [[ "$verbose" = "verbose" ]]; then echo "deploymentStatus1: $deploymentStatus1" echo "deploymentStatus2: $deploymentStatus2" fi echo "Unwrapping Data Kit components completed, with statuses $deploymentStatus1 and $deploymentStatus2" aid=$(sf data query -q "SELECT Id FROM ConnectedApplication WHERE Name = 'Integration'" --json | jq '.result.records.Id' -r) res=$(curl --location --request GET "$scDomain/$aid" --header "Cookie: sid=$scToken") r1=${res#*applicationId=} r2=${r1%\'); *} echo "r2: $r2" echo "Open the Connected App View page" sf org open -p "/app/mgmt/forceconnectedapps/forceAppDetail.apexp?applicationId=$r2" echo "Trying to open Connected App so that you can click Manage and Copy Consumer Details: Key and Secret" sf org open -p "/app/mgmt/forceconnectedapps/forceAppManageConsumer.apexp?applicationId=$r2" # Potentially we could try to follow the Manage Consumer Detail link to parse Key and Secret # However, it is been redirected, so we would have to fetch the redirection link first # res=$(curl --location --request GET "$iu/app/mgmt/forceconnectedapps/forceAppManageConsumer.apexp?applicationId=$r2" --header "Cookie: sid=$sid") # echo $res # r1=${res#*replace\(\'} # l1=${r1%\');*} # l2=${l1#*replace\(\'} # echo "link: $l2" # res2=$(curl --location --request GET "$iu/$l2" --header "Cookie: sid=$sid") # # which is in $l2 variable, then we would have to figure out how to avoid insufficient permission error # Currently if you follow this link from UI, it requires MFA for confirmation. IngestS.sh script scDomain=$(sf org display --json | jq '.result.instanceUrl' -r) echo "Sales Cloud domain: $scDomain" key=$1 secret=$2 verbose=$3 result=$( curl --location --request POST "$scDomain/services/oauth2/token" \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' --data-urlencode "client_id=$key" --data-urlencode "client_secret=$secret" ) if [[ "$verbose" = "verbose" ]]; then echo $result fi scToken=$(echo $result | jq '.access_token' -r) if [[ "$verbose" = "verbose" ]]; then echo $scToken fi result=$(curl --location --request POST "$scDomain//services/a360/token" \ --header 'Content-Type: application/x-www-form-urlencoded'\ --data-urlencode 'grant_type=urn:salesforce:grant-type:external:cdp'\ --data-urlencode "subject_token=$scToken"\ --data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token") if [[ "$verbose" = "verbose" ]]; then echo $result fi dcToken=$(echo $result | jq '.access_token' -r) dcDomain=$(echo $result | jq '.instance_url' -r) if [[ "$verbose" = "verbose" ]]; then echo $dcToken echo $dcDomain fi result=$(curl --location --request POST "https://$dcDomain/api/v1/ingest/sources/Looker/treatments_statuses/actions/test " \ --header "Authorization: Bearer $dcToken" \ --header 'Content-Type: application/json' \ --data @data/dc.json ) if [[ "$verbose" = "verbose" ]]; then echo "Ingest results: $result" fi result=$(curl --location --request POST "https://$dcDomain/api/v1/ingest/sources/Looker/treatments_statuses " \ --header "Authorization: Bearer $dcToken" \ --header 'Content-Type: application/json' \ --data @data/dc.json ) echo "Ingest results: $result" IngestB.sh script set -e set -o pipefail scDomain=$(sf org display --json | jq '.result.instanceUrl' -r) if [[ "$verbose" = "verbose" ]]; then echo "Sales Cloud domain: $scDomain" fi key=$1 secret=$2 verbose=$3 result=$( curl --location --request POST "$scDomain/services/oauth2/token" \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' --data-urlencode "client_id=$key" --data-urlencode "client_secret=$secret" ) if [[ "$verbose" = "verbose" ]]; then echo $result fi scToken=$(echo $result | jq '.access_token' -r) if [[ "$verbose" = "verbose" ]]; then echo $scToken fi result=$(curl --location --request POST "$scDomain//services/a360/token" \ --header 'Content-Type: application/x-www-form-urlencoded'\ --data-urlencode 'grant_type=urn:salesforce:grant-type:external:cdp'\ --data-urlencode "subject_token=$scToken"\ --data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token") if [[ "$verbose" = "verbose" ]]; then echo $result fi dcToken=$(echo $result | jq '.access_token' -r) dcDomain=$(echo $result | jq '.instance_url' -r) if [[ "$verbose" = "verbose" ]]; then echo $dcToken echo $dcDomain fi result=$(curl --location --request POST "https://$dcDomain/api/v1/ingest/jobs " \ --header "Authorization: Bearer $dcToken" \ --header 'Content-Type: application/json' \ --data '{"object":"patients", "sourceName":"Looker", "operation":"upsert"}' ) if [[ "$verbose" = "verbose" ]]; then echo "Job create request: $result" fi jid=$(echo $result | jq '.id' -r) if [[ "$verbose" = "verbose" ]]; then echo "jid: $jid" fi if [[ "$jid" = "null" ]]; then error=$(echo $result | jq '.error' -r) message=$(echo $result | jq '.message' -r) echo "Process halted because of error: $error with message: $message" exit 1 fi result=$(curl --location --request PUT "https://$dcDomain/api/v1/ingest/jobs/$jid/batches " \ --header 'Content-Type: text/csv' \ --header "Authorization: Bearer $dcToken" \ --data-binary @data/dcPat.csv ) if [[ "$verbose" = "verbose" ]]; then echo "Data upload results: $result" fi result=$(curl --location --request PATCH "https://$dcDomain/api/v1/ingest/jobs/$jid " \ --header 'Content-Type: application/json' \ --header "Authorization: Bearer $dcToken" \ --data '{"state" : "UploadComplete"}' ) if [[ "$verbose" = "verbose" ]]; then echo "Patch job results: $result" fi result=$(curl --location --request GET "https://$dcDomain/api/v1/ingest/jobs/$jid " \ --header 'Content-Type: application/json' \ --header "Authorization: Bearer $dcToken" ) if [[ "$verbose" = "verbose" ]]; then echo "get job status results: $result" fi state=$(echo $result | jq '.state' -r) echo "state: $state" while [[ "$state" != "JobComplete" ]]; do sleep 10 state=$(echo $result | jq '.state' -r) echo "state: $state" done echo "Final state: $state"
Asked by Patlatus (123 rep)
Jan 14, 2025, 11:09 AM
Last activity: Jan 15, 2025, 11:04 AM