การส่งผ่านตัวแปร bash ไปยัง jq


114

ฉันได้เขียนสคริปต์เพื่อดึงค่าบางอย่างจากfile.jsonไฟล์. ใช้งานได้ถ้าฉันให้ค่ากับ jq selectแต่ตัวแปรดูเหมือนจะไม่ทำงาน (หรือฉันไม่รู้วิธีใช้)

#!/bin/sh

#this works ***
projectID=$(cat file.json | jq -r '.resource[] | select(.username=="myemail@hotmail.com") | .id')
echo "$projectID"

EMAILID=myemail@hotmail.com

#this does not work *** no value is printed
projectID=$(cat file.json | jq -r '.resource[] | select(.username=="$EMAILID") | .id')
echo "$projectID"

ปัญหาที่เกี่ยวข้อง: การส่งตัวแปร bash ไปยังตัวกรอง jq มีไวยากรณ์ที่แตกต่างกันเล็กน้อยjq -r --arg var "$var" '.[$var]' stackoverflow.com/questions/34745451/…
enharmonic

คำตอบ:


182

พิจารณาการส่งผ่านตัวแปรเชลล์ (EMAILID) เป็นตัวแปร jq (ที่นี่ยัง EMAILID เพื่อประโยชน์ในการแสดงภาพประกอบ):

   projectID=$(cat file.json | 
     jq -r --arg EMAILID "$EMAILID" '
        .resource[]
        | select(.username==$EMAILID) 
        | .id')

Postscript

สำหรับเร็กคอร์ดความเป็นไปได้อีกประการหนึ่งคือการใช้envฟังก์ชันของ jq เพื่อเข้าถึงตัวแปรสภาพแวดล้อม ตัวอย่างเช่นพิจารณาลำดับของคำสั่ง bash นี้:

EMAILID=foo@bar.com  # not exported
EMAILID="$EMAILID" jq -n 'env.EMAILID'

เอาต์พุตเป็นสตริง JSON:

"foo@bar.com"

4
นี่เป็นคำตอบเดียวที่ปลอดภัย 100% ช่วยให้jqสามารถสร้างตัวกรองโดยใช้ค่าได้อย่างถูกต้องแทนที่จะใช้bashเพื่อสร้างสตริงที่jqตีความว่าเป็นตัวกรอง (พิจารณาว่าจะเกิดอะไรขึ้นถ้าค่าของEMAILIDประกอบด้วย a ).)
chepner

3
ขอบคุณสูงสุด โซลูชันที่คุณให้มาคือคำตอบที่เหมาะสมที่ฉันกำลังมองหา ใช่มันใช้งานได้
asidd

กรณีการใช้งานของฉันมันใช้งานได้! function n2 { termux-contact-list |jq -r --arg v1 "$1" '.[] | select(.name==$v1)|.number' }โทร:n2 Name1
Timo

2
fyi envฟังก์ชันของ jq จะเปลี่ยนไประหว่าง jq 1.4 และ 1.5+ ดังนั้นการอ้างอิงถึงenv.EMAILID(ในตัวอย่างคำตอบนี้) จึงไม่ทำงานใน jq 1.4 จึงแนะนำให้ใช้--arg EMAILID "$EMAILID"โครงสร้างหากคุณจำเป็นต้องใช้ jq 1.4 หวังว่านี่จะช่วยได้ แค่ใช้เวลา 1 วัน + คิดออกด้วยตัวเอง;)
m0j0hn

3
อาร์กิวเมนต์ที่ส่งผ่านโดยใช้--argจะถูกส่งผ่านเป็นสตริง หากคุณต้องการส่งผ่านข้อมูลประเภท JSON อื่นให้ใช้--argjsonซึ่งจะแยกวิเคราะห์สตริงเป็น JSON เช่น--argjson myObj '{"a": [1, 2]}'
BallpointBen

26

ฉันแก้ไขปัญหานี้โดยการหลีกเลี่ยงเครื่องหมายคำพูดคู่ภายใน

projectID=$(cat file.json | jq -r ".resource[] | select(.username==\"$EMAILID\") | .id")

ฉันใช้สิ่งนี้เพราะ --arg เล่นไม่ดีกับ-c
Dimitris Moraitidis

9

เป็นปัญหาในการเสนอราคาคุณต้อง:

projectID=$(
  cat file.json | jq -r ".resource[] | select(.username=='$EMAILID') | .id"
)

หากคุณใส่เครื่องหมายคำพูดเดี่ยวเพื่อคั่นสตริงหลักเชลล์จะใช้$EMAILIDตามตัวอักษร

"ดับเบิลอ้าง" ทุกตัวอักษรที่มีช่องว่าง / metacharacters และทุกการขยายตัว: "$var", "$(command "$var")", ,"${array[@]}" "a & b"ใช้'single quotes'สำหรับรหัสหรือตัวอักษร$'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. ดู
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words


ในกรณีของฉันเกิดข้อผิดพลาดเช่นกันหากไม่ใช้เครื่องหมายคำพูดเลย: termux-contact-list |jq -r '.[] | select(.name=='$1')|.number'. ผลลัพธ์:jq Compile error
Timo

6
ฉันไม่แน่ใจว่าทำไมความคิดเห็นนี้ถึงได้รับการโหวตหลายครั้ง ใช่เครื่องหมายคำพูดคู่อนุญาตให้ขยายตัวแปรได้ แต่jqไม่ชอบเครื่องหมายคำพูดเดี่ยว:jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?)
jdelman

หากอินพุตของคุณเป็น json ปกติค่าของมันจะเป็นสองเท่า (ไม่ใช่เดี่ยว) ที่ยกมา สิ่งที่ได้ผล: การใช้เครื่องหมายคำพูดคู่สำหรับอาร์กิวเมนต์ทั้งหมดของ jq และการหลีกเลี่ยง (ด้วย "\`) เครื่องหมายคำพูดคู่ใด ๆ ภายในเช่น @asid ตอบ
GuSuku

6

โพสต์ไว้ที่นี่เพราะอาจช่วยคนอื่นได้ ในสตริงอาจจำเป็นต้องส่งเครื่องหมายคำพูดไปที่ jq ในการทำสิ่งต่อไปนี้ด้วย jq:

.items[] | select(.name=="string")

ในการทุบตีคุณสามารถทำได้

EMAILID=$1
projectID=$(cat file.json | jq -r '.resource[] | select(.username=='\"$EMAILID\"') | .id')

หนีคำพูดเป็นหลักและส่งต่อไปยัง jq


ทำงานได้อย่างสมบูรณ์ ไม่ซับซ้อนเกินไปสำหรับคนที่กำลังมองหาวิธีแก้ปัญหาอย่างรวดเร็ว
Mo-Gang

4

อีกวิธีหนึ่งในการทำให้สำเร็จคือใช้แฟล็ก jq "--arg" โดยใช้ตัวอย่างเดิม:

#!/bin/sh

#this works ***
projectID=$(cat file.json | jq -r '.resource[] | 
select(.username=="myemail@hotmail.com") | .id')
echo "$projectID"

EMAILID=myemail@hotmail.com

# Use --arg to pass the variable to jq. This should work:
projectID=$(cat file.json | jq --arg EMAILID $EMAILID -r '.resource[] 
| select(.username=="$EMAILID") | .id')
echo "$projectID"

ดูที่นี่ซึ่งเป็นที่ที่ฉันพบโซลูชันนี้: https://github.com/stedolan/jq/issues/626


2

ฉันรู้ว่าช้าไปหน่อยที่จะตอบกลับขอโทษ แต่นั่นใช้ได้กับฉัน

export K8S_public_load_balancer_url="$(kubectl get services -n ${TENANT}-production -o wide | grep "ingress-nginx-internal$" | awk '{print $4}')"

และตอนนี้ฉันสามารถดึงและส่งผ่านเนื้อหาของตัวแปรไปยัง jq

export TF_VAR_public_load_balancer_url="$(aws elbv2 describe-load-balancers --region eu-west-1 | jq -r '.LoadBalancers[] | select (.DNSName == "'$K8S_public_load_balancer_url'") | .LoadBalancerArn')"

ในกรณีของฉันฉันต้องใช้เครื่องหมายคำพูดคู่และใบเสนอราคาเพื่อเข้าถึงค่าตัวแปร

ไชโย


1

ตอนนี้ Jq มีวิธีที่ดีกว่าในการเข้าถึงตัวแปรสภาพแวดล้อมคุณสามารถใช้ env.EMAILI:

projectID=$(cat file.json | jq -r ".resource[] | select(.username==env.EMAILID) | .id")

0

ไม่เกี่ยวข้องเล็กน้อย แต่ฉันจะยังคงใส่ไว้ที่นี่สำหรับวัตถุประสงค์อื่น ๆ ตัวแปรเชลล์สามารถใช้เป็น -

value=10
jq  '."key" = "'"$value"'"' file.json
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.