替换为转义的shell字符串?

时间:2018-05-31 17:36:10

标签: ruby bash

我想将ruby变量替换为转义的shell字符串。

这就是我的开始,它的确有效:

<<~`SHELL`
  curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" \
    -X POST \
    -H "Content-Type: application/json" \
    -d '{ "question": { "question_type": "multiple_choice_question", "question_text": "<p>Is everything clear?</p>", "points_possible": 1, "answers": [ { "text": "I read and understood", "html": "", "weight": 100 }, { "text": "I have questions", "comments_html": "<p>Please post your questions to a discussion (in course navigation).</p>", "weight": 0 } ] } }' \
    -H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL

但我希望discussion(在-d JSON中)成为链接。而且无法让它发挥作用:

myJson = %Q|{ "question": { "question_type": "multiple_choice_question", "question_text": "<p>Is everything clear?</p>", "points_possible": 1, "answers": [ { "text": "I read and understood", "html": "", "weight": 100 }, { "text": "I have questions", "comments_html": "<p>Please post your questions to a <a href="#{CANVAS_URI}/courses/#{COURSE_ID}/discussion_topics">discussion</a>.</p>", "weight": 0 } ] } }|

<<~`SHELL`
  curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" \
    -X POST \
    -H "Content-Type: application/json" \
    -d '#{myJson}' \
    -H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL

<<~`SHELL`
  curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" \
    -X POST \
    -H "Content-Type: application/json" \
    -d "#{myJson}" \
    -H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL

我被困在逃离两个郎之间。

整个事情是在ruby中发布嵌套json的解决方法。平面jsons适用于大多数http宝石,但是获得嵌套json是一种罕见的需求,我担心大多数宝石都没有测试它(我记得尝试使用http.rb来做到这一点,但即使是json数组也是{ {3}}

2 个答案:

答案 0 :(得分:3)

要解决您的问题,您必须小心使用Shellwords.escape来逃避所有问题,然后不要引用它们。

<<~`SHELL`
  curl -s "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions" \
    -X POST \
    -H "Content-Type: application/json" \
    -d #{Shellwords.escape(myJson)} \
    -H "Authorization: Bearer #{CANVAS_TOKEN}"
SHELL

但这很乏味且容易出错,而不是你的问题。

  

整个过程是在ruby中发布嵌套json的一种解决方法。平面jsons适用于大多数http宝石,但获得嵌套json是一种罕见的需求,我担心大多数宝石都没有测试它......

嵌套的JSON并不罕见,无论如何,http客户端都没有理由关心JSON的内容。它只是传输一个字符串。

您的JSON格式不正确。具体在这里:

"comments_html": "<p>Please post your questions to a <a href="#{CANVAS_URI}/courses/#{COURSE_ID}/discussion_topics">discussion</a>.</p>"

你有一个未转义的报价。这可能是因为您手动将嵌套的JSON放在一起。就像炮击卷曲一样,这很容易出错。

相反,创建一个Ruby哈希并将其转换为JSON。然后,您将获得Ruby语法检查的所有好处。

payload = {
  question: {
    question_type: "multiple_choice_question",
    question_text: "<p>Is everything clear?</p>",
    points_possible: 1,
    answers: [
      {
        text: "I read and understood", 
        html: "", 
        weight: 100
      }, {
        text: "I have questions",
        comments_html: %Q[<p>Please post your questions to a <a href="#{CANVAS_URI}/courses/#{COURSE_ID}/discussion_topics">discussion</a>.</p>],
        weight: 0
      }
    ]
  }
}

require "json"
json_payload = JSON.generate(payload)

而不是调用curl,而是使用http库。由于您正在调用REST API,因此可以使用RestClient

require "rest-client"
response = RestClient.post(
  "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions",
  json_payload,
  { content_type: :json, authorization: "Bearer #{CANVAS_TOKEN}" }
)

或者,更好的是,使用负责JSON转换的canvas-api gem并利用分页等API功能。

canvas = Canvas::API.new(:host => CANVAS_URI, :token => CANVAS_TOKEN)
response = canvas.post(
    "/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions",
    payload
)

答案 1 :(得分:3)

我认为你采取了错误的做法。将字符串插值与反引号或Kernel#system的单个参数版本混合是混乱和危险的。你最好完全绕过shell及其引用问题并使用system的多参数形式:

system(
  'curl',
  '-s', "#{CANVAS_URI}/api/v1/courses/#{COURSE_ID}/quizzes/#{quiz_id}/questions",
  '-X', 'POST',
  '-H', "Content-Type: application/json",
  '-d', myJson,
  '-H', "Authorization: Bearer #{CANVAS_TOKEN}"
)

这将使用提供的参数直接执行curl,而根本不涉及shell。如果您需要保存curl的响应,请使用标准库中的Open3,然后再次避免shell及其引用问题。