如何用autocannon POST命令运行clinic.js?执行报错求助
解决zsh下
clinic flame搭配带复杂POST体的autocannon命令时的event not found错误 问题原因
zsh的引号解析和历史事件扩展机制导致的:你用单引号包裹整个--on-port的参数内容,但其中autocannon的-b参数又用了单引号嵌套,这会让zsh提前终止外层单引号的解析,后续的括号等字符被误识别为历史事件查询,从而抛出event not found: )错误。单独运行autocannon时没有这个问题,是因为此时的引号嵌套解析逻辑和嵌套在--on-port参数里的场景不同。
解决方案
方案1:将POST请求体写入文件,避免命令行引号嵌套
- 创建
request-body.json文件,把原命令中-b后面的JSON内容复制进去:
{"operationName":"getEcosystemAdminSessionSummary","variables":{"input":{"pageNum":1,"limit":25,"sessionStatus":"UPCOMING","selectedFilters":[{"type":"num","values":["25"]}],"dateTimeSort":1}},"query":"query getEcosystemAdminSessionSummary($input: SessionSummaryV3Input!) {\n getEcosystemAdminSessionSummary(input: $input) {\n sessions {\n id\n subject\n subjectId\n sessionDescription\n status\n sessionType\n participants(needsFeedbackInfo: true) {\n user {\n id\n profile {\n firstName\n lastName\n profileId\n profilePicture {\n src\n __typename\n }\n phoneNumber\n status\n __typename\n }\n ... on PAASUser {\n ecosystemProfile {\n status\n __typename\n }\n __typename\n }\n email {\n address\n __typename\n }\n __typename\n }\n userType\n joinedTime\n joinedTimeDelta\n leftTime\n sessionPayoutStatus\n tutorBasePaymentAmount\n remaining\n hourlyRate\n hasPendingFeedback\n hasAssociatedFeedbackForm\n feedbackSubmitDate\n feedbackSubmitBy\n feedbackEditedBy\n participantFeedbackForm {\n id\n formType\n __typename\n }\n description\n __typename\n }\n numLeaders\n numLearners\n maxStudents\n tutorReviews {\n revieweeId\n __typename\n }\n date\n startTime\n endTime\n processingTime\n cancellationPolicyTime\n timeZoneId\n recurringDays\n recurringSessionId\n weeksRecurring\n weeksRepeat\n recurringStartDate\n recurringEndDate\n tutoringAmount\n meetingSolution\n isInPerson\n isPaid\n ecosystemId\n gradeLevel\n gradeLevels\n schools {\n id\n name\n __typename\n }\n joinUrl\n sessionCompletionStatus\n zoomOptions {\n tutorflyBookerId\n meetingId\n join_url\n host_id\n start_url\n password\n __typename\n }\n lessonspaceOptions {\n roomId\n __typename\n }\n uploadedFiles {\n method\n fileNames\n __typename\n }\n physicalSessionOptions {\n locationName\n locationPoint {\n type\n coordinates\n __typename\n }\n __typename\n }\n program {\n id\n name\n shortName\n sessions {\n sessionJoinMinutesPrior\n sessionRecordingsProfileTypes\n __typename\n }\n subBoard {\n enabled\n visibleToTutors\n __typename\n }\n __typename\n }\n subRequests {\n id\n userId\n status\n __typename\n }\n courseSection {\n name\n maxEnrollments\n enrolledStudentsAmt\n programs {\n id\n name\n sessions {\n sessionJoinMinutesPrior\n sessionRecordingsProfileTypes\n zoomEnabled\n __typename\n }\n __typename\n }\n perUserRate\n currentUserIsEnrolled\n __typename\n }\n sessionFiles {\n id\n filename\n url\n creator\n __typename\n }\n __typename\n }\n hasMore\n totalSessionCount\n __typename\n }\n}\n"}
- 修改
clinic flame命令,用-b @request-body.json读取文件内容:
clinic flame --on-port='autocannon -t 30 -c 100 -m POST -H "Authorization":"Bearer .eyJpZC" -H "review-app-secret-key":"9013a568aaeaa" -H "ecosystem-profile-id":"eco17" -H "Content-Type: application/json" -b @request-body.json http://localhost:7269/api/graphql' -- node ./dist/src/index.js
方案2:转义引号和特殊字符,适配zsh解析
如果不想用文件,需要对原命令中的引号和zsh特殊字符(比如!、$)进行转义:
- 将外层的单引号改为双引号
- 转义内部所有双引号(用
\") - 转义所有
$符号(用\$,避免zsh解析为变量) - 转义
!符号(用\!,避免触发历史事件扩展)
最终命令如下:
clinic flame --on-port="autocannon -t 30 -c 100 -m POST -H \"Authorization\":\"Bearer .eyJpZC\" -H \"review-app-secret-key\":\"9013a568aaeaa\" -H \"ecosystem-profile-id\":\"eco17\" -H \"Content-Type: application/json\" -b \"{\\\"operationName\\\":\\\"getEcosystemAdminSessionSummary\\\",\\\"variables\\\":{\\\"input\\\":{\\\"pageNum\\\":1,\\\"limit\\\":25,\\\"sessionStatus\\\":\\\"UPCOMING\\\",\\\"selectedFilters\\\":[{\\\"type\\\":\\\"num\\\",\\\"values\\\":[\\\"25\\\"]}],\\\"dateTimeSort\\\":1}},\\\"query\\\":\\\"query getEcosystemAdminSessionSummary(\\\$input: SessionSummaryV3Input!) {\\n getEcosystemAdminSessionSummary(input: \\\$input) {\\n sessions {\\n id\\n subject\\n subjectId\\n sessionDescription\\n status\\n sessionType\\n participants(needsFeedbackInfo: true) {\\n user {\\n id\\n profile {\\n firstName\\n lastName\\n profileId\\n profilePicture {\\n src\\n __typename\\n }\\n phoneNumber\\n status\\n __typename\\n }\\n ... on PAASUser {\\n ecosystemProfile {\\n status\\n __typename\\n }\\n __typename\\n }\\n email {\\n address\\n __typename\\n }\\n __typename\\n }\\n userType\\n joinedTime\\n joinedTimeDelta\\n leftTime\\n sessionPayoutStatus\\n tutorBasePaymentAmount\\n remaining\\n hourlyRate\\n hasPendingFeedback\\n hasAssociatedFeedbackForm\\n feedbackSubmitDate\\n feedbackSubmitBy\\n feedbackEditedBy\\n participantFeedbackForm {\\n id\\n formType\\n __typename\\n }\\n description\\n __typename\\n }\\n numLeaders\\n numLearners\\n maxStudents\\n tutorReviews {\\n revieweeId\\n __typename\\n }\n date\\n startTime\\n endTime\\n processingTime\\n cancellationPolicyTime\\n timeZoneId\\n recurringDays\\n recurringSessionId\\n weeksRecurring\\n weeksRepeat\\n recurringStartDate\\n recurringEndDate\\n tutoringAmount\\n meetingSolution\\n isInPerson\\n isPaid\\n ecosystemId\\n gradeLevel\\n gradeLevels\\n schools {\\n id\\n name\\n __typename\\n }\n joinUrl\\n sessionCompletionStatus\\n zoomOptions {\\n tutorflyBookerId\\n meetingId\\n join_url\\n host_id\\n start_url\\n password\\n __typename\\n }\n lessonspaceOptions {\\n roomId\\n __typename\\n }\n uploadedFiles {\\n method\\n fileNames\\n __typename\\n }\n physicalSessionOptions {\\n locationName\\n locationPoint {\\n type\\n coordinates\\n __typename\\n }\n __typename\\n }\n program {\\n id\\n name\\n shortName\\n sessions {\\n sessionJoinMinutesPrior\\n sessionRecordingsProfileTypes\\n __typename\\n }\n subBoard {\\n enabled\\n visibleToTutors\\n __typename\\n }\n __typename\\n }\n subRequests {\\n id\\n userId\\n status\\n __typename\\n }\n courseSection {\\n name\\n maxEnrollments\\n enrolledStudentsAmt\\n programs {\\n id\\n name\\n sessions {\\n sessionJoinMinutesPrior\\n sessionRecordingsProfileTypes\\n zoomEnabled\\n __typename\\n }\n __typename\\n }\n perUserRate\\n currentUserIsEnrolled\\n __typename\\n }\n sessionFiles {\\n id\\n filename\\n url\\n creator\\n __typename\\n }\n __typename\\n }\n hasMore\\n totalSessionCount\\n __typename\\n }\n}\\n\"}\" http://localhost:7269/api/graphql" -- node ./dist/src/index.js
方案3:用脚本封装autocannon命令
- 创建
load-test.sh脚本,内容如下:
#!/bin/bash autocannon -t 30 -c 100 -m POST \ -H "Authorization":"Bearer .eyJpZC" \ -H "review-app-secret-key":"9013a568aaeaa" \ -H "ecosystem-profile-id":"eco17" \ -H "Content-Type: application/json" \ -b '{"operationName":"getEcosystemAdminSessionSummary","variables":{"input":{"pageNum":1,"limit":25,"sessionStatus":"UPCOMING","selectedFilters":[{"type":"num","values":["25"]}],"dateTimeSort":1}},"query":"query getEcosystemAdminSessionSummary($input: SessionSummaryV3Input!) {\n getEcosystemAdminSessionSummary(input: $input) {\n sessions {\n id\n subject\n subjectId\n sessionDescription\n status\n sessionType\n participants(needsFeedbackInfo: true) {\n user {\n id\n profile {\n firstName\n lastName\n profileId\n profilePicture {\n src\n __typename\n }\n phoneNumber\n status\n __typename\n }\n ... on PAASUser {\n ecosystemProfile {\n status\n __typename\n }\n __typename\n }\n email {\n address\n __typename\n }\n __typename\n }\n userType\n joinedTime\n joinedTimeDelta\n leftTime\n sessionPayoutStatus\n tutorBasePaymentAmount\n remaining\n hourlyRate\n hasPendingFeedback\n hasAssociatedFeedbackForm\n feedbackSubmitDate\n feedbackSubmitBy\n feedbackEditedBy\n participantFeedbackForm {\n id\n formType\n __typename\n }\n description\n __typename\n }\n numLeaders\n numLearners\n maxStudents\n tutorReviews {\n revieweeId\n __typename\n }\n date\n startTime\n endTime\n processingTime\n cancellationPolicyTime\n timeZoneId\n recurringDays\n recurringSessionId\n weeksRecurring\n weeksRepeat\n recurringStartDate\n recurringEndDate\n tutoringAmount\n meetingSolution\n isInPerson\n isPaid\n ecosystemId\n gradeLevel\n gradeLevels\n schools {\n id\n name\n __typename\n }\n joinUrl\n sessionCompletionStatus\n zoomOptions {\n tutorflyBookerId\n meetingId\n join_url\n host_id\n start_url\n password\n __typename\n }\n lessonspaceOptions {\n roomId\n __typename\n }\n uploadedFiles {\n method\n fileNames\n __typename\n }\n physicalSessionOptions {\n locationName\n locationPoint {\n type\n coordinates\n __typename\n }\n __typename\n }\n program {\n id\n name\n shortName\n sessions {\n sessionJoinMinutesPrior\n sessionRecordingsProfileTypes\n __typename\n }\n subBoard {\n enabled\n visibleToTutors\n __typename\n }\n __typename\n }\n subRequests {\n id\n userId\n status\n __typename\n }\n courseSection {\n name\n maxEnrollments\n enrolledStudentsAmt\n programs {\n id\n name\n sessions {\n sessionJoinMinutesPrior\n sessionRecordingsProfileTypes\n zoomEnabled\n __typename\n }\n __typename\n }\n perUserRate\n currentUserIsEnrolled\n __typename\n }\n sessionFiles {\n id\n filename\n url\n creator\n __typename\n }\n __typename\n }\n hasMore\n totalSessionCount\n __typename\n }\n}\n"}' \ http://localhost:7269/api/graphql
- 给脚本添加执行权限:
chmod +x load-test.sh
- 运行
clinic flame命令调用脚本:
clinic flame --on-port='./load-test.sh' -- node ./dist/src/index.js
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

